added --status
This commit is contained in:
parent
9e5c5d38e5
commit
74db516798
|
|
@ -5,6 +5,8 @@ node_modules
|
|||
out
|
||||
dist
|
||||
*.tgz
|
||||
csharpierd
|
||||
csharpierd.exe
|
||||
|
||||
# code coverage
|
||||
coverage
|
||||
|
|
|
|||
204
README.md
204
README.md
|
|
@ -1,15 +1,211 @@
|
|||
# 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
|
||||
bun install
|
||||
```
|
||||
|
||||
To run:
|
||||
## Usage
|
||||
|
||||
### Command Line Options
|
||||
|
||||
```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 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 {
|
||||
pid: 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
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
23
package.json
23
package.json
|
|
@ -1,12 +1,33 @@
|
|||
{
|
||||
"name": "csharpierd",
|
||||
"version": "1.0.0",
|
||||
"description": "A persistent CSharpier formatting daemon with automatic server management and idle timeout",
|
||||
"module": "index.ts",
|
||||
"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": {
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"engines": {
|
||||
"bun": ">=1.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue