decorated-pi 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,237 +1,157 @@
1
1
  /**
2
- * LSP Server Config — language detection, server commands, workspace roots
3
- *
4
- * Based on @spences10/pi-lsp by Scott Spence
5
- * https://github.com/spences10/my-pi/tree/main/packages/pi-lsp (MIT License)
6
- *
7
- * Modifications: added C/C++ (clangd) and Lua support
2
+ * LSP Server Config — language detection, server commands, workspace roots.
8
3
  */
9
4
  import { existsSync } from "node:fs";
10
5
  import { dirname, extname, isAbsolute, join, resolve } from "node:path";
11
6
 
7
+ // ─── File extension → language mapping ────────────────────────────────────
8
+
12
9
  const EXTENSION_LANGUAGES: Record<string, string> = {
13
- ".ts": "typescript",
14
- ".tsx": "typescript",
15
- ".mts": "typescript",
16
- ".cts": "typescript",
17
- ".js": "typescript",
18
- ".jsx": "typescript",
19
- ".mjs": "typescript",
20
- ".cjs": "typescript",
21
- ".c": "c",
22
- ".h": "cpp",
23
- ".cc": "cpp",
24
- ".cp": "cpp",
25
- ".cpp": "cpp",
26
- ".cxx": "cpp",
27
- ".hh": "cpp",
28
- ".hpp": "cpp",
29
- ".hxx": "cpp",
30
- ".ipp": "cpp",
31
- ".ixx": "cpp",
32
- ".tpp": "cpp",
33
- ".py": "python",
34
- ".rs": "rust",
35
- ".go": "go",
36
- ".rb": "ruby",
37
- ".java": "java",
38
- ".lua": "lua",
39
- ".svelte": "svelte",
10
+ ".ts": "typescript", ".tsx": "typescript", ".mts": "typescript", ".cts": "typescript",
11
+ ".js": "typescript", ".jsx": "typescript", ".mjs": "typescript", ".cjs": "typescript",
12
+ ".c": "c", ".h": "cpp", ".cc": "cpp", ".cp": "cpp", ".cpp": "cpp",
13
+ ".cxx": "cpp", ".hh": "cpp", ".hpp": "cpp", ".hxx": "cpp",
14
+ ".py": "python", ".rs": "rust", ".go": "go", ".rb": "ruby",
15
+ ".java": "java", ".lua": "lua", ".svelte": "svelte",
16
+ ".json": "json",
40
17
  };
41
18
 
42
- export type LanguageConfig = {
19
+ export interface LanguageConfig {
43
20
  language: string;
44
21
  command: string;
45
22
  args: string[];
46
23
  install_hint: string;
47
24
  is_project_local?: boolean;
48
- };
25
+ }
49
26
 
50
27
  const LANGUAGE_SERVERS: Record<string, Omit<LanguageConfig, "is_project_local">> = {
51
28
  typescript: {
52
- language: "typescript",
53
- command: "typescript-language-server",
29
+ language: "typescript", command: "typescript-language-server",
54
30
  args: ["--stdio"],
55
- install_hint:
56
- "Install TypeScript LSP with: pnpm add -D typescript typescript-language-server",
31
+ install_hint: "Install TypeScript LSP with: pnpm add -D typescript typescript-language-server",
57
32
  },
58
33
  c: {
59
- language: "c",
60
- command: "clangd",
61
- args: ["--background-index"],
34
+ language: "c", command: "clangd", args: ["--background-index"],
62
35
  install_hint: "Install clangd and ensure the clangd binary is on PATH.",
63
36
  },
64
37
  cpp: {
65
- language: "cpp",
66
- command: "clangd",
67
- args: ["--background-index"],
38
+ language: "cpp", command: "clangd", args: ["--background-index"],
68
39
  install_hint: "Install clangd and ensure the clangd binary is on PATH.",
69
40
  },
70
41
  python: {
71
- language: "python",
72
- command: "pylsp",
73
- args: [],
42
+ language: "python", command: "pylsp", args: [],
74
43
  install_hint: "Install Python LSP with: pip install python-lsp-server",
75
44
  },
76
45
  rust: {
77
- language: "rust",
78
- command: "rust-analyzer",
79
- args: [],
80
- install_hint:
81
- "Install Rust Analyzer and ensure the rust-analyzer binary is on PATH.",
46
+ language: "rust", command: "rust-analyzer", args: [],
47
+ install_hint: "Install Rust Analyzer and ensure the rust-analyzer binary is on PATH.",
82
48
  },
83
49
  go: {
84
- language: "go",
85
- command: "gopls",
86
- args: ["serve"],
87
- install_hint:
88
- "Install Go LSP with: go install golang.org/x/tools/gopls@latest",
50
+ language: "go", command: "gopls", args: ["serve"],
51
+ install_hint: "Install Go LSP with: go install golang.org/x/tools/gopls@latest",
89
52
  },
90
53
  ruby: {
91
- language: "ruby",
92
- command: "solargraph",
93
- args: ["stdio"],
54
+ language: "ruby", command: "solargraph", args: ["stdio"],
94
55
  install_hint: "Install Ruby LSP with: gem install solargraph",
95
56
  },
96
57
  java: {
97
- language: "java",
98
- command: "jdtls",
99
- args: [],
100
- install_hint:
101
- "Install Eclipse JDT Language Server and ensure the jdtls binary is on PATH.",
58
+ language: "java", command: "jdtls", args: [],
59
+ install_hint: "Install Eclipse JDT Language Server and ensure the jdtls binary is on PATH.",
102
60
  },
103
61
  lua: {
104
- language: "lua",
105
- command: "lua-language-server",
106
- args: [],
107
- install_hint:
108
- "Install Lua LSP and ensure the lua-language-server binary is on PATH.",
62
+ language: "lua", command: "lua-language-server", args: [],
63
+ install_hint: "Install Lua LSP and ensure the lua-language-server binary is on PATH.",
109
64
  },
110
65
  svelte: {
111
- language: "svelte",
112
- command: "svelteserver",
113
- args: ["--stdio"],
114
- install_hint:
115
- "Install Svelte LSP with: pnpm add -D svelte-language-server (or volta install svelte-language-server)",
66
+ language: "svelte", command: "svelteserver", args: ["--stdio"],
67
+ install_hint: "Install Svelte LSP with: pnpm add -D svelte-language-server",
68
+ },
69
+ json: {
70
+ language: "json", command: "biome", args: ["lsp-proxy"],
71
+ install_hint: "Install Biome with: pnpm add -D @biomejs/biome",
116
72
  },
117
73
  };
118
74
 
119
75
  const WORKSPACE_MARKERS = [
120
- "svelte.config.js",
121
- "svelte.config.ts",
122
- "tsconfig.json",
123
- "jsconfig.json",
124
- "package.json",
125
- "pyproject.toml",
126
- "Cargo.toml",
127
- "go.mod",
128
- "Gemfile",
129
- "pom.xml",
130
- "build.gradle",
131
- "build.gradle.kts",
132
- ".clangd",
133
- "compile_commands.json",
134
- "compile_flags.txt",
135
- "CMakeLists.txt",
136
- "meson.build",
137
- "Makefile",
76
+ "svelte.config.js", "svelte.config.ts", "tsconfig.json", "jsconfig.json",
77
+ "package.json", "pyproject.toml", "Cargo.toml", "go.mod",
78
+ "Gemfile", "pom.xml", "build.gradle", "build.gradle.kts",
79
+ ".clangd", "compile_commands.json", "compile_flags.txt",
80
+ "CMakeLists.txt", "meson.build", "Makefile",
138
81
  ];
139
82
 
140
83
  const REPOSITORY_MARKERS = [
141
- "pnpm-workspace.yaml",
142
- "package-lock.json",
143
- "yarn.lock",
144
- "bun.lockb",
145
- "bun.lock",
146
- ".git",
84
+ "pnpm-workspace.yaml", "package-lock.json", "yarn.lock",
85
+ "bun.lockb", "bun.lock", ".git",
147
86
  ];
148
87
 
149
- export function detect_language(file_path: string): string | undefined {
150
- return EXTENSION_LANGUAGES[extname(file_path).toLowerCase()];
151
- }
88
+ // ─── Public API ───────────────────────────────────────────────────────────
152
89
 
153
- export function list_supported_languages(): string[] {
154
- return Object.keys(LANGUAGE_SERVERS).sort();
90
+ export function detectLanguage(filePath: string): string | undefined {
91
+ return EXTENSION_LANGUAGES[extname(filePath).toLowerCase()];
155
92
  }
156
93
 
157
- function resolve_server_command_info(
158
- command: string,
159
- cwd: string = process.cwd()
160
- ): { command: string; is_project_local: boolean } {
161
- if (
162
- !command ||
163
- isAbsolute(command) ||
164
- command.includes("/") ||
165
- command.includes("\\")
166
- ) {
167
- return { command, is_project_local: false };
168
- }
169
- for (const dir of ancestor_directories(cwd)) {
170
- const local_bin = resolve_local_binary(dir, command);
171
- if (local_bin) {
172
- return { command: local_bin, is_project_local: true };
173
- }
174
- }
175
- return { command, is_project_local: false };
94
+ export function listSupportedLanguages(): string[] {
95
+ return Object.keys(LANGUAGE_SERVERS).sort();
176
96
  }
177
97
 
178
- export function get_server_config(
98
+ export function getServerConfig(
179
99
  language: string,
180
- cwd: string = process.cwd()
100
+ cwd = process.cwd(),
181
101
  ): LanguageConfig | undefined {
182
102
  const base = LANGUAGE_SERVERS[language];
183
103
  if (!base) return undefined;
184
- const resolved = resolve_server_command_info(base.command, cwd);
104
+
105
+ const resolved = resolveLocalBinary(base.command, cwd);
185
106
  return { ...base, command: resolved.command, is_project_local: resolved.is_project_local };
186
107
  }
187
108
 
188
- export function language_id_for_file(file_path: string): string | undefined {
189
- return detect_language(file_path);
109
+ export function languageIdForFile(filePath: string): string | undefined {
110
+ return detectLanguage(filePath);
190
111
  }
191
112
 
192
- export function find_workspace_root(
193
- file_path: string,
194
- fallback: string = process.cwd()
113
+ export function findWorkspaceRoot(
114
+ filePath: string,
115
+ fallback = process.cwd(),
195
116
  ): string {
196
- const start = resolve(dirname(file_path));
197
- const project_root = find_nearest_marker_directory(start, WORKSPACE_MARKERS);
198
- if (project_root) return project_root;
199
- const repo_root = find_nearest_marker_directory(start, REPOSITORY_MARKERS);
200
- if (repo_root) return repo_root;
117
+ const start = resolve(dirname(filePath));
118
+ const projectRoot = findNearestMarker(start, WORKSPACE_MARKERS);
119
+ if (projectRoot) return projectRoot;
120
+ const repoRoot = findNearestMarker(start, REPOSITORY_MARKERS);
121
+ if (repoRoot) return repoRoot;
201
122
  return resolve(fallback);
202
123
  }
203
124
 
204
- function find_nearest_marker_directory(
205
- start: string,
206
- markers: string[]
207
- ): string | undefined {
208
- for (const dir of ancestor_directories(start)) {
209
- if (markers.some((marker) => existsSync(join(dir, marker)))) {
210
- return dir;
211
- }
125
+ // ─── Internal helpers ─────────────────────────────────────────────────────
126
+
127
+ function resolveLocalBinary(
128
+ command: string,
129
+ cwd: string,
130
+ ): { command: string; is_project_local: boolean } {
131
+ if (isAbsolute(command) || command.includes("/") || command.includes("\\")) {
132
+ return { command, is_project_local: false };
133
+ }
134
+ for (const dir of ancestorDirs(cwd)) {
135
+ const localBin = join(dir, "node_modules", ".bin", command);
136
+ if (existsSync(localBin)) return { command: localBin, is_project_local: true };
137
+ if (existsSync(localBin + ".cmd")) return { command: localBin + ".cmd", is_project_local: true };
138
+ }
139
+ return { command, is_project_local: false };
140
+ }
141
+
142
+ function findNearestMarker(start: string, markers: string[]): string | undefined {
143
+ for (const dir of ancestorDirs(start)) {
144
+ if (markers.some((m) => existsSync(join(dir, m)))) return dir;
212
145
  }
213
146
  return undefined;
214
147
  }
215
148
 
216
- function ancestor_directories(start: string): string[] {
217
- const dirs: string[] = [];
149
+ function* ancestorDirs(start: string): Generator<string> {
218
150
  let current = resolve(start);
219
151
  while (true) {
220
- dirs.push(current);
152
+ yield current;
221
153
  const parent = dirname(current);
222
154
  if (parent === current) break;
223
155
  current = parent;
224
156
  }
225
- return dirs;
226
- }
227
-
228
- function resolve_local_binary(
229
- directory: string,
230
- command: string
231
- ): string | undefined {
232
- const candidates = [
233
- join(directory, "node_modules", ".bin", command),
234
- join(directory, "node_modules", ".bin", `${command}.cmd`),
235
- ];
236
- return candidates.find((candidate) => existsSync(candidate));
237
157
  }