added --status
This commit is contained in:
parent
9e5c5d38e5
commit
74db516798
|
|
@ -5,6 +5,8 @@ node_modules
|
||||||
out
|
out
|
||||||
dist
|
dist
|
||||||
*.tgz
|
*.tgz
|
||||||
|
csharpierd
|
||||||
|
csharpierd.exe
|
||||||
|
|
||||||
# code coverage
|
# code coverage
|
||||||
coverage
|
coverage
|
||||||
|
|
|
||||||
204
README.md
204
README.md
|
|
@ -1,15 +1,211 @@
|
||||||
# csharpierd
|
# csharpierd
|
||||||
|
|
||||||
To install dependencies:
|
A persistent CSharpier formatting daemon with automatic server management and idle timeout.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Starts CSharpier server in background on first use
|
||||||
|
- Reuses existing server for subsequent formatting requests
|
||||||
|
- Automatically shuts down after 1 hour of inactivity
|
||||||
|
- Thread-safe with file locking mechanism
|
||||||
|
- Auto-recovery if server crashes
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- [Bun](https://bun.sh) runtime (>= 1.0.0)
|
||||||
|
- [CSharpier](https://csharpier.com/) installed globally or locally
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Global Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Bun
|
||||||
|
bun install -g csharpierd
|
||||||
|
|
||||||
|
# npm
|
||||||
|
npm install -g csharpierd
|
||||||
|
|
||||||
|
# Yarn
|
||||||
|
yarn global add csharpierd
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm install -g csharpierd
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun install
|
bun install
|
||||||
```
|
```
|
||||||
|
|
||||||
To run:
|
## Usage
|
||||||
|
|
||||||
|
### Command Line Options
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run index.ts
|
csharpierd <filename> < input.cs # Format C# code from stdin
|
||||||
|
csharpierd --status # Show server status
|
||||||
|
csharpierd --stop # Stop the background server
|
||||||
|
csharpierd --help # Show help message
|
||||||
```
|
```
|
||||||
|
|
||||||
This project was created using `bun init` in bun v1.3.0. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
### As Global Command
|
||||||
|
|
||||||
|
After global installation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Format a C# file
|
||||||
|
csharpierd Program.cs < Program.cs
|
||||||
|
|
||||||
|
# Or using cat
|
||||||
|
cat MyFile.cs | csharpierd MyFile.cs
|
||||||
|
|
||||||
|
# Output formatted code to a new file
|
||||||
|
csharpierd MyFile.cs < MyFile.cs > MyFile.formatted.cs
|
||||||
|
|
||||||
|
# Check server status
|
||||||
|
csharpierd --status
|
||||||
|
|
||||||
|
# Stop the background server
|
||||||
|
csharpierd --stop
|
||||||
|
|
||||||
|
# Show help
|
||||||
|
csharpierd --help
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Format a file
|
||||||
|
bun index.ts Program.cs < Program.cs
|
||||||
|
|
||||||
|
# Check server status
|
||||||
|
bun index.ts --status
|
||||||
|
|
||||||
|
# Stop the server
|
||||||
|
bun index.ts --stop
|
||||||
|
|
||||||
|
# Show help
|
||||||
|
bun index.ts --help
|
||||||
|
```
|
||||||
|
|
||||||
|
### Server Management
|
||||||
|
|
||||||
|
#### Check Server Status
|
||||||
|
|
||||||
|
The `--status` flag shows detailed information about the server including:
|
||||||
|
|
||||||
|
- Running state (RUNNING, STARTING, STOPPED, or NOT RUNNING)
|
||||||
|
- Process ID and port
|
||||||
|
- Last access time
|
||||||
|
- Idle time with color-coded warnings (green < 75% timeout, yellow >= 75%, red >= 100%)
|
||||||
|
- Configuration details
|
||||||
|
|
||||||
|
```bash
|
||||||
|
csharpierd --status
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Stopping the Server
|
||||||
|
|
||||||
|
The server will automatically shut down after 1 hour of inactivity, but you can manually stop it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
csharpierd --stop
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
You can compile the TypeScript code to a standalone binary:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run build
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a `csharpierd` binary in the current directory that can be distributed without requiring Bun to be installed. The binary is self-contained and includes all dependencies.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run the compiled binary
|
||||||
|
./csharpierd Program.cs < Program.cs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Editor Integration
|
||||||
|
|
||||||
|
### Neovim with conform.nvim
|
||||||
|
|
||||||
|
[conform.nvim](https://github.com/stevearc/conform.nvim) is a popular formatter plugin for Neovim. Here's how to configure it to use `csharpierd`:
|
||||||
|
|
||||||
|
#### Basic Configuration
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require("conform").setup({
|
||||||
|
formatters_by_ft = {
|
||||||
|
cs = { "csharpierd" },
|
||||||
|
},
|
||||||
|
formatters = {
|
||||||
|
csharpierd = {
|
||||||
|
command = "csharpierd",
|
||||||
|
args = { "$FILENAME" },
|
||||||
|
stdin = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### With Lazy.nvim
|
||||||
|
|
||||||
|
```lua
|
||||||
|
{
|
||||||
|
"stevearc/conform.nvim",
|
||||||
|
event = { "BufWritePre" },
|
||||||
|
cmd = { "ConformInfo" },
|
||||||
|
opts = {
|
||||||
|
formatters_by_ft = {
|
||||||
|
cs = { "csharpierd" },
|
||||||
|
},
|
||||||
|
formatters = {
|
||||||
|
csharpierd = {
|
||||||
|
command = "csharpierd",
|
||||||
|
args = { "$FILENAME" },
|
||||||
|
stdin = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
format_on_save = {
|
||||||
|
timeout_ms = 5000,
|
||||||
|
lsp_fallback = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Benefits of using csharpierd with conform.nvim
|
||||||
|
|
||||||
|
- **Fast formatting**: Reuses the CSharpier server process, avoiding startup overhead
|
||||||
|
- **Automatic server management**: Server starts on first use and stops after 1 hour of inactivity
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. **First Call**: Starts `dotnet csharpier server --server-port 78912` in the background
|
||||||
|
2. **Subsequent Calls**: Reuses the existing server process
|
||||||
|
3. **Idle Timeout**: Server automatically shuts down after 1 hour of inactivity
|
||||||
|
4. **State Management**: Server state (PID, port, last access time) stored in `/tmp/csharpierd-state.json`
|
||||||
|
5. **Concurrency**: Lock file prevents race conditions when multiple instances run simultaneously
|
||||||
|
|
||||||
|
## Server Details
|
||||||
|
|
||||||
|
- **Port**: 78912 (hardcoded)
|
||||||
|
- **State File**: `/tmp/csharpierd-state.json`
|
||||||
|
- **Lock File**: `/tmp/csharpierd.lock`
|
||||||
|
- **Idle Timeout**: 1 hour (3600000ms)
|
||||||
|
|
||||||
|
## Publishing
|
||||||
|
|
||||||
|
To publish this package to npm:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun publish
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
|
||||||
147
index.ts
147
index.ts
|
|
@ -5,6 +5,18 @@ const LOCK_FILE = "/tmp/csharpierd.lock";
|
||||||
const SERVER_PORT = 18912;
|
const SERVER_PORT = 18912;
|
||||||
const IDLE_TIMEOUT_MS = 60 * 60 * 1000; // 1 hour
|
const IDLE_TIMEOUT_MS = 60 * 60 * 1000; // 1 hour
|
||||||
|
|
||||||
|
// Color utilities using Bun.color
|
||||||
|
const RESET = "\x1b[0m";
|
||||||
|
const BOLD = "\x1b[1m";
|
||||||
|
|
||||||
|
const colorize = (text: string, color: string): string => {
|
||||||
|
return `${Bun.color(color, "ansi")}${text}${RESET}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const bold = (text: string): string => {
|
||||||
|
return `${BOLD}${text}${RESET}`;
|
||||||
|
};
|
||||||
|
|
||||||
interface ServerState {
|
interface ServerState {
|
||||||
pid: number;
|
pid: number;
|
||||||
port: number;
|
port: number;
|
||||||
|
|
@ -221,11 +233,142 @@ async function formatCode(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show help message
|
||||||
|
function showHelp(): void {
|
||||||
|
console.log(`csharpierd - CSharpier formatting daemon
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
csharpierd <filename> < input.cs Format C# code from stdin
|
||||||
|
csharpierd --status Show server status
|
||||||
|
csharpierd --stop Stop the background server
|
||||||
|
csharpierd --help Show this help message
|
||||||
|
|
||||||
|
Description:
|
||||||
|
A persistent CSharpier formatting daemon that starts a background server
|
||||||
|
on first use and reuses it for subsequent formatting requests. The server
|
||||||
|
automatically shuts down after 1 hour of inactivity.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
# Format a C# file
|
||||||
|
csharpierd Program.cs < Program.cs
|
||||||
|
|
||||||
|
# Format and save to a new file
|
||||||
|
csharpierd MyFile.cs < MyFile.cs > MyFile.formatted.cs
|
||||||
|
|
||||||
|
# Using cat
|
||||||
|
cat Program.cs | csharpierd Program.cs
|
||||||
|
|
||||||
|
# Check server status
|
||||||
|
csharpierd --status
|
||||||
|
|
||||||
|
# Stop the background server
|
||||||
|
csharpierd --stop
|
||||||
|
|
||||||
|
Server Details:
|
||||||
|
Port: ${SERVER_PORT}
|
||||||
|
State File: ${STATE_FILE}
|
||||||
|
Lock File: ${LOCK_FILE}
|
||||||
|
Idle Timeout: ${IDLE_TIMEOUT_MS / 1000 / 60} minutes
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the server
|
||||||
|
async function stopServer(): Promise<void> {
|
||||||
|
const state = await loadState();
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
console.error("No server is currently running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(`Stopping CSharpier server (PID ${state.pid})...`);
|
||||||
|
await killServer(state.pid);
|
||||||
|
await Bun.$`rm -f ${STATE_FILE} ${LOCK_FILE}`.quiet();
|
||||||
|
console.error("Server stopped successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show server status
|
||||||
|
async function showStatus(): Promise<void> {
|
||||||
|
const state = await loadState();
|
||||||
|
|
||||||
|
console.log(bold("\nCSharpier Server Status"));
|
||||||
|
console.log("═".repeat(50));
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
console.log(colorize("Status:", "cyan"), colorize("NOT RUNNING", "red"));
|
||||||
|
console.log("\nNo server is currently active.");
|
||||||
|
console.log("The server will start automatically on the first format request.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if process is actually running
|
||||||
|
const isRunning = await isProcessRunning(state.pid);
|
||||||
|
const isResponsive = isRunning ? await isServerResponsive(state.port) : false;
|
||||||
|
|
||||||
|
if (isRunning && isResponsive) {
|
||||||
|
console.log(colorize("Status:", "cyan"), colorize("RUNNING", "green"));
|
||||||
|
} else if (isRunning && !isResponsive) {
|
||||||
|
console.log(colorize("Status:", "cyan"), colorize("STARTING", "yellow"));
|
||||||
|
} else {
|
||||||
|
console.log(colorize("Status:", "cyan"), colorize("STOPPED", "red"));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(colorize("PID:", "cyan"), state.pid);
|
||||||
|
console.log(colorize("Port:", "cyan"), state.port);
|
||||||
|
|
||||||
|
// Calculate and display uptime
|
||||||
|
const now = Date.now();
|
||||||
|
const lastAccess = new Date(state.lastAccess);
|
||||||
|
const idleTime = now - state.lastAccess;
|
||||||
|
const idleMinutes = Math.floor(idleTime / 1000 / 60);
|
||||||
|
const idleSeconds = Math.floor((idleTime / 1000) % 60);
|
||||||
|
|
||||||
|
console.log(colorize("Last Access:", "cyan"), lastAccess.toLocaleString());
|
||||||
|
|
||||||
|
const idleTimeStr = `${idleMinutes}m ${idleSeconds}s`;
|
||||||
|
const timeoutMinutes = IDLE_TIMEOUT_MS / 1000 / 60;
|
||||||
|
|
||||||
|
if (idleMinutes >= timeoutMinutes) {
|
||||||
|
console.log(colorize("Idle Time:", "cyan"), colorize(idleTimeStr, "red"), "(will shutdown)");
|
||||||
|
} else if (idleMinutes >= timeoutMinutes * 0.75) {
|
||||||
|
console.log(colorize("Idle Time:", "cyan"), colorize(idleTimeStr, "yellow"), `(${timeoutMinutes - idleMinutes}m until timeout)`);
|
||||||
|
} else {
|
||||||
|
console.log(colorize("Idle Time:", "cyan"), colorize(idleTimeStr, "green"));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(colorize("State File:", "cyan"), STATE_FILE);
|
||||||
|
console.log(colorize("Lock File:", "cyan"), LOCK_FILE);
|
||||||
|
console.log(colorize("Idle Timeout:", "cyan"), `${timeoutMinutes} minutes`);
|
||||||
|
console.log("");
|
||||||
|
}
|
||||||
|
|
||||||
// Main
|
// Main
|
||||||
async function main() {
|
async function main() {
|
||||||
const fileName = process.argv[2];
|
const arg = process.argv[2];
|
||||||
|
|
||||||
|
// Handle --help flag
|
||||||
|
if (arg === "--help" || arg === "-h") {
|
||||||
|
showHelp();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle --status flag
|
||||||
|
if (arg === "--status") {
|
||||||
|
await showStatus();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle --stop flag
|
||||||
|
if (arg === "--stop") {
|
||||||
|
await stopServer();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal formatting mode
|
||||||
|
const fileName = arg;
|
||||||
if (!fileName) {
|
if (!fileName) {
|
||||||
console.error("Usage: bun index.ts <filename> < input.cs");
|
console.error("Usage: csharpierd <filename> < input.cs");
|
||||||
|
console.error("Try 'csharpierd --help' for more information");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
23
package.json
23
package.json
|
|
@ -1,12 +1,33 @@
|
||||||
{
|
{
|
||||||
"name": "csharpierd",
|
"name": "csharpierd",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A persistent CSharpier formatting daemon with automatic server management and idle timeout",
|
||||||
"module": "index.ts",
|
"module": "index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"bin": {
|
||||||
|
"csharpierd": "./index.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"format": "bun index.ts",
|
||||||
|
"build": "bun build --compile --minify ./index.ts --outfile csharpierd"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"csharpier",
|
||||||
|
"formatter",
|
||||||
|
"csharp",
|
||||||
|
"dotnet",
|
||||||
|
"daemon",
|
||||||
|
"persistent"
|
||||||
|
],
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest"
|
"@types/bun": "latest"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"bun": ">=1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue