@likec4/language-server 1.57.0 → 1.58.0
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.
- package/dist/_chunks/LikeC4FileSystem.mjs +2 -2
- package/dist/_chunks/module.d.mts +424 -424
- package/dist/_chunks/module.mjs +24 -24
- package/dist/_chunks/utils.mjs +1 -1
- package/package.json +23 -23
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{t as e}from"./icons.mjs";import{isLikeC4Builtin as t}from"../likec4lib.mjs";import{c as n,
|
|
1
|
+
import{t as e}from"./icons.mjs";import{isLikeC4Builtin as t}from"../likec4lib.mjs";import{c as n,ii as r,l as i,n as a,o,oi as s,s as c,ui as l}from"./utils.mjs";import{compareNaturalHierarchically as u,onNextTick as d,stringHash as f}from"@likec4/core/utils";import{isLikeC4Config as p,loadConfig as m}from"@likec4/config/node";import{fdir as h}from"fdir";import{Disposable as g,SimpleCache as _,URI as v,UriUtils as y}from"langium";import{NodeFileSystemProvider as b}from"langium/node";import{mkdirSync as x,statSync as S}from"node:fs";import{unlink as C,writeFile as w}from"node:fs/promises";import{basename as T,dirname as E}from"node:path";import D from"chokidar";import O from"p-queue";import{exact as k}from"@likec4/core";import A from"json5";import j from"p-limit";import{indexBy as M,prop as N}from"remeda";import{Position as P,Range as F}from"vscode-languageserver-types";const I=l.getChild(`manual-layouts`),L=`.likec4.snap`,isManualLayoutFile=e=>e!==L&&e.endsWith(L);function fileName(e){return`${e}${L}`}function viewIdFromURI(e){let t=y.basename(e);return isManualLayoutFile(t)?t.slice(0,-12):null}function getManualLayoutsOutDir(e){return y.resolvePath(e.folderUri,e.config.manualLayouts?.outDir??`.likec4`)}const R={manualLayouts:e=>new DefaultLikeC4ManualLayouts(e)},z=`file://./`;var DefaultLikeC4ManualLayouts=class extends r{services;cache;listeners=[];#e=j(1);constructor(e){super(),this.services=e,this.cache=new _,this.onDispose(g.create(()=>{this.listeners.length=0,this.cache.clear()})),d(()=>{this.onDispose(e.workspace.ProjectsManager.onProjectsUpdate(()=>{this.clearCaches()}))})}async handleFileSystemUpdate(e){let t=e.update??e.delete,n=viewIdFromURI(t)??void 0,r=this.services.workspace.ProjectsManager.ownerProjectId(t);if(this.cache.delete(r),`delete`in e){this.triggerUpdate(k({removed:t,projectId:r,viewId:n}));return}let i=this.services.workspace.ProjectsManager.getProject(r);if(!n){let e=await this.readSnapshot(t,i);e?n=e.id:(I.error(`Snapshot ${t.fsPath} does not exist or is invalid`),n=`index`)}this.triggerUpdate({updated:t,projectId:r,viewId:n})}onManualLayoutUpdate(e){return this.listeners.push(e),{dispose:()=>{let t=this.listeners.indexOf(e);t!==-1&&this.listeners.splice(t,1)}}}async readManualLayouts(e){let t=I.getChild(e.id),n=this.services.workspace.FileSystemProvider,r=getManualLayoutsOutDir(e),i=[],a=`${r.toString()}`;try{let o=await n.scanDirectory(r,isManualLayoutFile);if(o.length===0)return null;o.sort((e,t)=>e.uri.path.localeCompare(t.uri.path));for(let r of o)try{let t=await n.readFile(r.uri),o=A.parse(t);a=f(a+r.uri.toString()+t);let s=this.resolveIconPathsAfterRead(o,e.folderUri);i.push({...s,_layout:`manual`})}catch(e){t.warn(`Failed to read view snapshot ${r.uri.fsPath}`,{err:e})}i.length&&t.trace`read manual layouts for ${e.id}, found ${i.length}`}catch(n){t.warn(`Failed to read manual layouts for ${e.folderUri.fsPath}`,{err:n})}if(i.length===0)return null;let o=M(i,N(`id`));return{hash:a,views:o}}async readSnapshot(e,t){let n=this.services.workspace.FileSystemProvider;try{let r=await n.readFile(e),i=A.parse(r);if(!t){let n=this.services.workspace.ProjectsManager.ownerProjectId(e);t=this.services.workspace.ProjectsManager.getProject(n)}return{...this.resolveIconPathsAfterRead(i,t.folderUri),_layout:`manual`}}catch(t){return I.warn(`Failed to read view snapshot ${e.fsPath}`,{err:t}),null}}async read(e){return await this.#e(async()=>{let t=this.cache.get(e.id);if(t!==void 0)return I.trace`cache hit project ${e.id}`,t;let n=await this.readManualLayouts(e);return this.cache.set(e.id,n),n})}async write(e,t){this.cache.delete(e.id);let n=I.getChild(e.id),r=getManualLayoutsOutDir(e),i=y.joinPath(r,fileName(t.id));if(`manualLayout`in t){let{manualLayout:e,...n}=t;t=n}let a=A.stringify(this.normalizeIconPathsForWrite(t,e.folderUri),{space:2,quote:`'`}),o={uri:i.toString(),range:F.create(P.create(0,0),P.create(a.split(`
|
|
2
2
|
`).length-1,1))};n.debug`write snapshot of ${t.id} in project ${e.id} to ${i.fsPath}`;let s=this.services.workspace.FileSystemProvider;try{await s.writeFile(i,a+`
|
|
3
|
-
`),this.triggerUpdate({updated:i,projectId:e.id,viewId:t.id})}catch(e){n.warn(`Failed to write snapshot ${t.id} to ${i.fsPath}`,{err:e})}return o}async remove(e,t){this.cache.delete(e.id);let n=
|
|
3
|
+
`),this.triggerUpdate({updated:i,projectId:e.id,viewId:t.id})}catch(e){n.warn(`Failed to write snapshot ${t.id} to ${i.fsPath}`,{err:e})}return o}async remove(e,t){this.cache.delete(e.id);let n=I.getChild(e.id),r=getManualLayoutsOutDir(e),i=y.joinPath(r,fileName(t));n.debug`delete snapshot of ${t} in project ${e.id}. File: ${i.fsPath}`;let a={uri:i.toString(),range:F.create(0,0,0,0)};try{if(!await this.services.workspace.FileSystemProvider.deleteFile(i))return n.warn`Snapshot ${t} did not exist at ${i.fsPath}`,null;this.triggerUpdate({removed:i,projectId:e.id,viewId:t})}catch(e){n.warn(`Failed to delete snapshot ${t} from ${i.fsPath}`,{err:e})}return a}clearCaches(){I.trace`clear caches`,this.cache.clear()}triggerUpdate(e){let t=[...this.listeners];for(let n of t)a(()=>n(e))}normalizeIconPathsForWrite(e,t){let n=e.nodes.map(e=>{if(!e.icon||typeof e.icon!=`string`)return e;if(e.icon.startsWith(`file://`)){let n=v.parse(e.icon),r=y.relative(t,n);return r.startsWith(`..`)?e:{...e,icon:`${z}${r}`}}return e});return{...e,nodes:n}}resolveIconPathsAfterRead(e,t){let n=e.nodes.map(e=>{if(!e.icon||typeof e.icon!=`string`)return e;if(e.icon.startsWith(z)){let n=e.icon.substring(9),r=y.joinPath(t,n);return{...e,icon:r.toString()}}return e});return{...e,nodes:n}}};const B=l.getChild(`chokidar`),V={fileSystemWatcher:e=>new ChokidarFileSystemWatcher(e)},isAnyLikeC4File=e=>{let t=T(e);return c(t)||p(t)||isManualLayoutFile(t)};var ChokidarFileSystemWatcher=class{services;watcher;queue=new O({concurrency:1,timeout:5e3});constructor(e){this.services=e,B.debug`ChokidarFileSystemWatcher created`}watch(e){if(this.watcher){B.debug`add watching folder: ${e}`,this.watcher.add(e);return}this.watcher=this.createWatcher(e)}async dispose(){if(this.watcher){let e=this.watcher;this.watcher=void 0;try{await e.close()}catch{}}}createWatcher(e){B.debug`create watcher for folder: ${e}`;let t=D.watch(e,{ignored:[e=>n(e),(e,t)=>!!t&&t.isFile()&&!isAnyLikeC4File(e)],followSymlinks:!0,ignoreInitial:!0}),onAddOrChange=(e,t)=>{t?.isDirectory()||this.enqueueFileOp(`addOrChange: `+e,async()=>{await this.onAddOrChange(e)})};return t.on(`add`,onAddOrChange).on(`change`,onAddOrChange).on(`unlink`,(e,t)=>{t?.isDirectory()||this.enqueueFileOp(`remove: `+e,async()=>{await this.onRemove(e)})}).on(`unlinkDir`,e=>{this.enqueueFileOp(`removeDir: `+e,async()=>{await this.onRemoveDir(e)})}),t}enqueueFileOp(e,t){this.queue.add(async()=>{try{await t()}catch(t){B.warn(`Failed on {fileop}`,{fileop:e,error:t})}}).catch(t=>{B.error(`Error on {fileop}`,{fileop:e,error:t})})}async onAddOrChange(e){let t=this.services.workspace,n=T(e),r=v.file(e);switch(!0){case p(n):B.debug`project config changed: ${e}`,t.ManualLayouts.clearCaches(),await t.ProjectsManager.registerConfigFile(r);break;case isManualLayoutFile(n):B.debug`manual layout file changed: ${e}`,await t.ManualLayouts.handleFileSystemUpdate({update:r});break;case c(n):B.debug`file changed: ${e}`,await t.DocumentBuilder.update([r],[]);break;default:B.warn`Unknown file change: ${e}`}}async onRemove(e){let t=this.services.workspace,n=T(e),r=v.file(e);switch(!0){case p(n):B.debug`project file removed: ${e}`,t.ManualLayouts.clearCaches(),await t.ProjectsManager.reloadProjects();break;case c(n):B.debug`file removed: ${e}`,await t.DocumentBuilder.update([],[r]);break;case isManualLayoutFile(n):B.debug`manual layout file removed: ${e}`,await t.ManualLayouts.handleFileSystemUpdate({delete:r});break;default:B.warn`Unknown file removal: ${e}`}}async onRemoveDir(e){B.debug`directory removed: ${e}`;let t=this.services.workspace;t.ProjectsManager.findOverlaped(e).length>0&&(t.ManualLayouts.clearCaches(),await t.ProjectsManager.reloadProjects())}};const H=l.getChild(`filesystem`);function isLikeC4ConfigFile(e,t=!1){return!t&&p(T(e))}function isLikeC4File(e,t=!1){return!t&&c(T(e))}const WithFileSystem=(e=!1)=>({fileSystemProvider:()=>new SymLinkTraversingFileSystemProvider,...e?V:s});var SymLinkTraversingFileSystemProvider=class extends b{async readFile(n){if(t(n))return Promise.resolve(e);try{return await super.readFile(n)}catch(e){return H.warn(`Failed to read file ${n.fsPath}`,{error:e}),``}}async readDirectory(e,t){let n=t?.recursive??!0,r=t?.maxDepth??1/0,a=[];try{let t=new h().withSymlinks({resolvePaths:!1}).exclude(i).withFullPaths().filter(isLikeC4File);n?r!==1/0&&(t=t.withMaxDepth(r)):t=t.withMaxDepth(1);let o=await t.crawl(e.fsPath).withPromise();for(let e of o)a.push({isFile:!0,isDirectory:!1,uri:v.file(e)})}catch(t){H.warn(`Failed to read directory ${e.fsPath}`,{error:t})}return a.sort(o)}async scanProjectFiles(e){let t=await this.scanDirectory(e,isLikeC4ConfigFile);if(t.length<=1)return t;let n=u(`/`,!0);return t.sort((e,t)=>n(e.uri.path,t.uri.path))}async scanDirectory(e,t){let n=[];try{let r=await new h().withSymlinks({resolvePaths:!1}).exclude(i).withFullPaths().filter(t).crawl(e.fsPath).withPromise();for(let e of r)n.push({isFile:!0,isDirectory:!1,uri:v.file(e)})}catch(t){H.warn(`Failed to scan directory {path}`,{path:e.fsPath,error:t})}return n}async loadProjectConfig(e){return await m(e)}async writeFile(e,t){let n=E(e.fsPath),r=S(n,{throwIfNoEntry:!1});if(r?.isFile())throw Error(`Cannot create directory ${n} because a file with the same name exists.`);return r||(H.debug(`creating directory {path}`,{path:n}),x(n,{recursive:!0})),H.debug(`writing file {path}`,{path:e.fsPath}),await w(e.fsPath,t,{encoding:`utf-8`})}async deleteFile(e){try{let t=e.fsPath,n=S(t,{throwIfNoEntry:!1});return n?.isFile()||n?.isSymbolicLink()?(await C(t),H.debug(`deleted file {path}`,{path:t}),!0):(H.warn(`deleteFile failed: {path} does not exist, or is not a file`,{path:t}),!1)}catch(t){H.warn(`Failed to delete file ${e.fsPath}`,{error:t})}return!1}};export{V as n,R as r,WithFileSystem as t};
|