fish-lsp
Version:
LSP implementation for fish/fish-shell
317 lines (316 loc) • 10.2 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.logger = exports.Logger = exports.LogLevel = exports.DEFAULT_LOG_LEVEL = exports.LOG_LEVELS = void 0;
exports.createServerLogger = createServerLogger;
const console = __importStar(require("node:console"));
const fs_1 = __importDefault(require("fs"));
const config_1 = require("./config");
exports.LOG_LEVELS = ['error', 'warning', 'info', 'debug', 'log', ''];
exports.DEFAULT_LOG_LEVEL = 'log';
exports.LogLevel = {
error: 1,
warning: 2,
info: 3,
debug: 4,
log: 5,
'': 6,
};
function getLogLevel(level) {
if (exports.LOG_LEVELS.includes(level)) {
return level;
}
return exports.DEFAULT_LOG_LEVEL;
}
class Logger {
_console = console;
_silence = false;
_clear = true;
_logQueue = [];
logFilePath = '';
started = false;
isConnectedToConnection = false;
requiresConnectionConsole = true;
_logLevel = '';
constructor(logFilePath = '') {
this.logFilePath = logFilePath;
this.log = this.log.bind(this);
this.debug = this.debug.bind(this);
this.info = this.info.bind(this);
this.warning = this.warning.bind(this);
this.error = this.error.bind(this);
this._log = this._log.bind(this);
this.convertArgsToString = this.convertArgsToString.bind(this);
this._logWithSeverity = this._logWithSeverity.bind(this);
this.logAsJson = this.logAsJson.bind(this);
this.logFallbackToStdout = this.logFallbackToStdout.bind(this);
}
setLogFilePath(logFilePath) {
this.logFilePath = logFilePath;
return this;
}
setConnectionConsole(_console) {
if (_console) {
this._console = _console;
this.isConnectedToConnection = true;
}
return this;
}
setConsole(_console) {
if (_console) {
this._console = _console;
}
return this;
}
setClear(clear = true) {
this._clear = clear;
return this;
}
setSilent(silence = true) {
this._silence = silence;
return this;
}
setLogLevel(level) {
const logLevel = getLogLevel(level);
if (exports.LOG_LEVELS.includes(logLevel)) {
this._logLevel = logLevel;
}
return this;
}
allowDefaultConsole() {
this.requiresConnectionConsole = false;
return this;
}
isConnectionConsole() {
return this.isConnectedToConnection;
}
isStarted() {
return this.started;
}
isSilent() {
return this._silence;
}
isClearing() {
return this._clear;
}
isConnected() {
return this.isConnectedToConnection && this.requiresConnectionConsole;
}
hasLogLevel() {
return this._logLevel !== '';
}
hasConsole() {
if (this.isConnectionConsole()) {
return this.isConnected();
}
return this._console !== undefined;
}
start() {
this.started = true;
this.clearLogFile();
this._logQueue.forEach((message) => {
this._log(message);
});
return this;
}
hasLogFile() {
return this.logFilePath !== '';
}
clearLogFile() {
if (this.isClearing() && this.hasLogFile()) {
try {
fs_1.default.writeFileSync(this.logFilePath, '');
}
catch (error) {
this._console.error(`Error clearing log file: ${error}`);
}
}
}
convertArgsToString(...args) {
if (!args || args.length === 0) {
return '';
}
const formattedArgs = args.map(arg => this.formatArgument(arg));
return formattedArgs.length === 1
? formattedArgs.at(0) || ''
: formattedArgs.join('\n');
}
formatArgument(arg) {
if (arg === null)
return 'null';
if (arg === undefined)
return 'undefined';
if (arg instanceof Error) {
return arg.stack || arg.message || String(arg);
}
if (typeof arg === 'string')
return arg;
if (typeof arg !== 'object')
return String(arg);
if (arg instanceof Date) {
return arg.toISOString();
}
if (Array.isArray(arg)) {
if (arg.length === 0)
return '[]';
if (arg.length < 5 && arg.every(item => item === null ||
item === undefined ||
typeof item !== 'object')) {
return JSON.stringify(arg);
}
}
try {
const seen = new WeakSet();
return JSON.stringify(arg, (key, value) => {
if (typeof value === 'function') {
return '[Function]';
}
if (value instanceof RegExp) {
return value.toString();
}
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular Reference]';
}
seen.add(value);
}
return value;
}, 2);
}
catch (err) {
try {
const className = arg.constructor?.name || 'Object';
const properties = Object.keys(arg).length > 0
? `with ${Object.keys(arg).length} properties`
: 'empty';
return `[${className}: ${properties}]`;
}
catch {
return '[Object: stringify failed]';
}
}
}
_log(...args) {
if (!args)
return;
if (!this.isSilent() && this.hasConsole())
this._console.log(...args);
const formattedMessage = this.convertArgsToString(...args);
if (this.hasLogFile()) {
fs_1.default.appendFileSync(this.logFilePath, formattedMessage + '\n', 'utf-8');
}
else {
this._logQueue.push(formattedMessage);
}
}
logAsJson(...args) {
if (!args || args.some(arg => !arg))
return;
const formattedMessage = this.convertArgsToString(args);
this._log({
date: new Date().toLocaleString(),
message: formattedMessage,
});
}
_logWithSeverity(severity, ...args) {
if (this.hasLogLevel() && exports.LogLevel[this._logLevel] < exports.LogLevel[severity]) {
return;
}
const formattedMessage = [severity.toUpperCase() + ':', this.convertArgsToString(...args)].join(' ');
this._log(formattedMessage);
}
logPropertiesForEachObject(objs, ...keys) {
objs.forEach((obj, i) => {
const selectedKeys = keys.filter(key => Object.prototype.hasOwnProperty.bind(obj, key));
const selectedObj = selectedKeys.reduce((acc, key) => {
acc[key] = obj[key];
return acc;
}, {});
const formattedMessage = `${i}: ${JSON.stringify(selectedObj, null, 2)}`;
this._log(formattedMessage);
});
}
logTime(...args) {
const formattedMessage = this.convertArgsToString(...args);
const time = new Date().toLocaleTimeString();
this._log(`[${time}] ${formattedMessage}`);
}
log(...args) {
if (!args)
return;
const formattedMessage = this.convertArgsToString(...args);
if (!this.hasLogLevel()) {
this._log(formattedMessage);
return;
}
this._logWithSeverity('log', formattedMessage);
}
debug(...args) {
this._logWithSeverity('debug', ...args);
}
info(...args) {
this._logWithSeverity('info', ...args);
}
warning(...args) {
this._logWithSeverity('warning', ...args);
}
error(...args) {
this._logWithSeverity('error', ...args);
}
logToStdout(message, newline = true) {
const newlineChar = newline ? '\n' : '';
const output = `${message}${newlineChar}`;
process.stdout.write(output);
}
logToStdoutJoined(...message) {
const output = `${message.join('')}\n`;
process.stdout.write(output);
}
logToStderr(message, newline = true) {
const output = `${message}${!!newline && '\n'}`;
process.stderr.write(output);
}
logFallbackToStdout(...args) {
if (this.isStarted()) {
this.log(...args);
}
else {
this.logToStdout(JSON.stringify(args, null, 2), true);
}
}
}
exports.Logger = Logger;
exports.logger = new Logger();
function createServerLogger(logFilePath, connectionConsole) {
return exports.logger
.setLogFilePath(logFilePath)
.setConnectionConsole(connectionConsole)
.setSilent()
.setLogLevel(config_1.config.fish_lsp_log_level)
.start();
}