@likec4/language-server 1.55.0 → 1.56.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.
@@ -1,3 +1,3 @@
1
- import{t as e}from"./icons.mjs";import{isLikeC4Builtin as t}from"../likec4lib.mjs";import{c as n,ci as r,n as i,o as a,oi as o,s}from"./utils.mjs";import{isLikeC4Config as c,loadConfig as l}from"@likec4/config/node";import{fdir as u}from"fdir";import{SimpleCache as d,URI as f,UriUtils as p}from"langium";import{NodeFileSystemProvider as m}from"langium/node";import{mkdirSync as h,statSync as g}from"node:fs";import{unlink as _,writeFile as v}from"node:fs/promises";import{basename as y,dirname as b}from"node:path";import{objectHash as x,onNextTick as S}from"@likec4/core/utils";import C from"chokidar";import w from"p-queue";import{exact as T}from"@likec4/core";import E from"json5";import D from"p-limit";import{indexBy as O,prop as k}from"remeda";import{Position as A,Range as j}from"vscode-languageserver-types";const M=o.getChild(`manual-layouts`),N=`.likec4.snap`,isManualLayoutFile=e=>e.endsWith(N);function fileName(e){return`${e}${N}`}function viewIdFromURI(e){let t=p.basename(e);return isManualLayoutFile(t)?t.slice(0,-12):null}function getManualLayoutsOutDir(e){return p.resolvePath(e.folderUri,e.config.manualLayouts?.outDir??`.likec4`)}const P={manualLayouts:e=>new DefaultLikeC4ManualLayouts(e)},F=`file://./`;var DefaultLikeC4ManualLayouts=class{cache;listeners=[];#e=D(1);constructor(e){this.services=e,this.cache=new d,S(()=>{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(T({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:(M.error(`File ${t} does not exist or is not a valid manual layout file`),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=M.getChild(e.id),n=this.services.workspace.FileSystemProvider,r=getManualLayoutsOutDir(e),i=[];try{let a=await n.scanDirectory(r,isManualLayoutFile);if(a.length===0)return null;for(let r of a)try{let t=await n.readFile(r.uri),a=E.parse(t),o=this.resolveIconPathsAfterRead(a,e.folderUri);i.push({...o,_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 a=O(i,k(`id`));return{hash:x(a),views:a}}async readSnapshot(e,t){let n=this.services.workspace.FileSystemProvider;try{let r=await n.readFile(e),i=E.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 M.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 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=M.getChild(e.id),r=getManualLayoutsOutDir(e),i=p.joinPath(r,fileName(t.id));if(`manualLayout`in t){let{manualLayout:e,...n}=t;t=n}let a=E.stringify(this.normalizeIconPathsForWrite(t,e.folderUri),{space:2,quote:`'`}),o={uri:i.toString(),range:j.create(A.create(0,0),A.create(a.split(`
1
+ import{t as e}from"./icons.mjs";import{isLikeC4Builtin as t}from"../likec4lib.mjs";import{c as n,ci as r,l as i,n as a,o,oi as s,s as c}from"./utils.mjs";import{isLikeC4Config as l,loadConfig as u}from"@likec4/config/node";import{compareNaturalHierarchically as d,objectHash as f,onNextTick as p}from"@likec4/core/utils";import{fdir as m}from"fdir";import{SimpleCache as h,URI as g,UriUtils as _}from"langium";import{NodeFileSystemProvider as v}from"langium/node";import{mkdirSync as y,statSync as b}from"node:fs";import{unlink as x,writeFile as S}from"node:fs/promises";import{basename as C,dirname as w}from"node:path";import T from"chokidar";import E from"p-queue";import{exact as D}from"@likec4/core";import O from"json5";import k from"p-limit";import{indexBy as A,prop as j}from"remeda";import{Position as M,Range as N}from"vscode-languageserver-types";const P=s.getChild(`manual-layouts`),F=`.likec4.snap`,isManualLayoutFile=e=>e!==F&&e.endsWith(F);function fileName(e){return`${e}${F}`}function viewIdFromURI(e){let t=_.basename(e);return isManualLayoutFile(t)?t.slice(0,-12):null}function getManualLayoutsOutDir(e){return _.resolvePath(e.folderUri,e.config.manualLayouts?.outDir??`.likec4`)}const I={manualLayouts:e=>new DefaultLikeC4ManualLayouts(e)},L=`file://./`;var DefaultLikeC4ManualLayouts=class{cache;listeners=[];#e=k(1);constructor(e){this.services=e,this.cache=new h,p(()=>{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(D({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:(P.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=P.getChild(e.id),n=this.services.workspace.FileSystemProvider,r=getManualLayoutsOutDir(e),i=[];try{let a=await n.scanDirectory(r,isManualLayoutFile);if(a.length===0)return null;for(let r of a)try{let t=await n.readFile(r.uri),a=O.parse(t),o=this.resolveIconPathsAfterRead(a,e.folderUri);i.push({...o,_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 a=A(i,j(`id`));return{hash:f(a),views:a}}async readSnapshot(e,t){let n=this.services.workspace.FileSystemProvider;try{let r=await n.readFile(e),i=O.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 P.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 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=P.getChild(e.id),r=getManualLayoutsOutDir(e),i=_.joinPath(r,fileName(t.id));if(`manualLayout`in t){let{manualLayout:e,...n}=t;t=n}let a=O.stringify(this.normalizeIconPathsForWrite(t,e.folderUri),{space:2,quote:`'`}),o={uri:i.toString(),range:N.create(M.create(0,0),M.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=M.getChild(e.id),r=getManualLayoutsOutDir(e),i=p.joinPath(r,fileName(t));n.debug`delete snapshot of ${t} in project ${e.id}. File: ${i.fsPath}`;let a={uri:i.toString(),range:j.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(){M.trace`clear caches`,this.cache.clear()}triggerUpdate(e){for(let t of this.listeners)i(()=>t(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=f.parse(e.icon),r=p.relative(t,n);return r.startsWith(`..`)?e:{...e,icon:`${F}${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(F)){let n=e.icon.substring(9),r=p.joinPath(t,n);return{...e,icon:r.toString()}}return e});return{...e,nodes:n}}};const I=o.getChild(`chokidar`),L={fileSystemWatcher:e=>new ChokidarFileSystemWatcher(e)},isAnyLikeC4File=e=>{let t=y(e);return n(t)||c(t)||isManualLayoutFile(t)};var ChokidarFileSystemWatcher=class{watcher;queue=new w({concurrency:1,timeout:5e3});constructor(e){this.services=e}watch(e){if(this.watcher){I.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,await e.close()}}createWatcher(e){I.debug`create watcher for folder: ${e}`;let t=C.watch(e,{ignored:[e=>e.includes(`node_modules`)||e.includes(`.git`),(e,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){I.warn(`Failed on {fileop}`,{fileop:e,error:t})}}).catch(t=>{I.error(`Error on {fileop}`,{fileop:e,error:t})})}async onAddOrChange(e){let t=this.services.workspace,r=y(e),i=f.file(e);switch(!0){case c(r):I.debug`project file changed: ${e}`,t.ManualLayouts.clearCaches(),await t.ProjectsManager.registerConfigFile(i);break;case n(r):I.debug`file changed: ${e}`,await t.DocumentBuilder.update([i],[]);break;case isManualLayoutFile(r):I.debug`manual layout file changed: ${e}`,t.ManualLayouts.clearCaches(),await t.ManualLayouts.handleFileSystemUpdate({update:i});break;default:I.warn`Unknown file change: ${e}`}}async onRemove(e){let t=this.services.workspace,r=y(e),i=f.file(e);switch(!0){case c(r):I.debug`project file removed: ${e}`,t.ManualLayouts.clearCaches(),await t.ProjectsManager.reloadProjects();break;case n(r):I.debug`file removed: ${e}`,await t.DocumentBuilder.update([],[i]);break;case isManualLayoutFile(r):I.debug`manual layout file removed: ${e}`,await t.ManualLayouts.handleFileSystemUpdate({delete:i});break;default:I.warn`Unknown file removal: ${e}`}}async onRemoveDir(e){I.debug`directory removed: ${e}`;let t=this.services.workspace;t.ProjectsManager.findOverlaped(e).length>0&&(t.ManualLayouts.clearCaches(),await t.ProjectsManager.reloadProjects())}};const R=o.getChild(`filesystem`);function isLikeC4ConfigFile(e,t=!1){return!t&&c(y(e))}function isLikeC4File(e,t=!1){return!t&&n(y(e))}const WithFileSystem=(e=!0)=>({fileSystemProvider:()=>new SymLinkTraversingFileSystemProvider,...e?L:r});var SymLinkTraversingFileSystemProvider=class extends m{async readFile(n){if(t(n))return Promise.resolve(e);try{return await super.readFile(n)}catch(e){return R.warn(`Failed to read file ${n.fsPath}`,{error:e}),``}}async readDirectory(e,t){let n=t?.recursive??!0,r=t?.maxDepth??1/0,i=[];try{let t=new u().withSymlinks({resolvePaths:!1}).exclude(s).withFullPaths().filter(isLikeC4File);n?r!==1/0&&(t=t.withMaxDepth(r)):t=t.withMaxDepth(1);let a=await t.crawl(e.fsPath).withPromise();for(let e of a)i.push({isFile:!0,isDirectory:!1,uri:f.file(e)})}catch(t){R.warn(`Failed to read directory ${e.fsPath}`,{error:t})}return i.sort(a)}async scanProjectFiles(e){return await this.scanDirectory(e,isLikeC4ConfigFile)}async scanDirectory(e,t){let n=[];try{let r=await new u().withSymlinks({resolvePaths:!1}).exclude(s).withFullPaths().filter(t).crawl(e.fsPath).withPromise();for(let e of r)n.push({isFile:!0,isDirectory:!1,uri:f.file(e)})}catch(t){R.warn(`Failed to scan directory {path}`,{path:e.fsPath,error:t})}return n}async loadProjectConfig(e){return await l(e)}async writeFile(e,t){let n=b(e.fsPath),r=g(n,{throwIfNoEntry:!1});if(r?.isFile())throw Error(`Cannot create directory ${n} because a file with the same name exists.`);return r||(R.debug(`creating directory {path}`,{path:n}),h(n,{recursive:!0})),R.debug(`writing file {path}`,{path:e.fsPath}),await v(e.fsPath,t,{encoding:`utf-8`})}async deleteFile(e){try{let t=e.fsPath,n=g(t,{throwIfNoEntry:!1});return n?.isFile()||n?.isSymbolicLink()?(await _(t),R.debug(`deleted file {path}`,{path:t}),!0):(R.warn(`deleteFile failed: {path} does not exist, or is not a file`,{path:t}),!1)}catch(t){R.warn(`Failed to delete file ${e.fsPath}`,{error:t})}return!1}};export{L as n,P as r,WithFileSystem as t};
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=P.getChild(e.id),r=getManualLayoutsOutDir(e),i=_.joinPath(r,fileName(t));n.debug`delete snapshot of ${t} in project ${e.id}. File: ${i.fsPath}`;let a={uri:i.toString(),range:N.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(){P.trace`clear caches`,this.cache.clear()}triggerUpdate(e){for(let t of this.listeners)a(()=>t(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=g.parse(e.icon),r=_.relative(t,n);return r.startsWith(`..`)?e:{...e,icon:`${L}${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(L)){let n=e.icon.substring(9),r=_.joinPath(t,n);return{...e,icon:r.toString()}}return e});return{...e,nodes:n}}};const R=s.getChild(`chokidar`),z={fileSystemWatcher:e=>new ChokidarFileSystemWatcher(e)},isAnyLikeC4File=e=>{let t=C(e);return c(t)||l(t)||isManualLayoutFile(t)};var ChokidarFileSystemWatcher=class{watcher;queue=new E({concurrency:1,timeout:5e3});constructor(e){this.services=e}watch(e){if(this.watcher){R.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,await e.close()}}createWatcher(e){R.debug`create watcher for folder: ${e}`;let t=T.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){R.warn(`Failed on {fileop}`,{fileop:e,error:t})}}).catch(t=>{R.error(`Error on {fileop}`,{fileop:e,error:t})})}async onAddOrChange(e){let t=this.services.workspace,n=C(e),r=g.file(e);switch(!0){case l(n):R.debug`project config changed: ${e}`,t.ManualLayouts.clearCaches(),await t.ProjectsManager.registerConfigFile(r);break;case isManualLayoutFile(n):R.debug`manual layout file changed: ${e}`,await t.ManualLayouts.handleFileSystemUpdate({update:r});break;case c(n):R.debug`file changed: ${e}`,await t.DocumentBuilder.update([r],[]);break;default:R.warn`Unknown file change: ${e}`}}async onRemove(e){let t=this.services.workspace,n=C(e),r=g.file(e);switch(!0){case l(n):R.debug`project file removed: ${e}`,t.ManualLayouts.clearCaches(),await t.ProjectsManager.reloadProjects();break;case c(n):R.debug`file removed: ${e}`,await t.DocumentBuilder.update([],[r]);break;case isManualLayoutFile(n):R.debug`manual layout file removed: ${e}`,await t.ManualLayouts.handleFileSystemUpdate({delete:r});break;default:R.warn`Unknown file removal: ${e}`}}async onRemoveDir(e){R.debug`directory removed: ${e}`;let t=this.services.workspace;t.ProjectsManager.findOverlaped(e).length>0&&(t.ManualLayouts.clearCaches(),await t.ProjectsManager.reloadProjects())}};const B=s.getChild(`filesystem`);function isLikeC4ConfigFile(e,t=!1){return!t&&l(C(e))}function isLikeC4File(e,t=!1){return!t&&c(C(e))}const WithFileSystem=(e=!0)=>({fileSystemProvider:()=>new SymLinkTraversingFileSystemProvider,...e?z:r});var SymLinkTraversingFileSystemProvider=class extends v{async readFile(n){if(t(n))return Promise.resolve(e);try{return await super.readFile(n)}catch(e){return B.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 m().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:g.file(e)})}catch(t){B.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=d(`/`,!0);return t.sort((e,t)=>n(e.uri.path,t.uri.path))}async scanDirectory(e,t){let n=[];try{let r=await new m().withSymlinks({resolvePaths:!1}).exclude(i).withFullPaths().filter(t).crawl(e.fsPath).withPromise();for(let e of r)n.push({isFile:!0,isDirectory:!1,uri:g.file(e)})}catch(t){B.warn(`Failed to scan directory {path}`,{path:e.fsPath,error:t})}return n}async loadProjectConfig(e){return await u(e)}async writeFile(e,t){let n=w(e.fsPath),r=b(n,{throwIfNoEntry:!1});if(r?.isFile())throw Error(`Cannot create directory ${n} because a file with the same name exists.`);return r||(B.debug(`creating directory {path}`,{path:n}),y(n,{recursive:!0})),B.debug(`writing file {path}`,{path:e.fsPath}),await S(e.fsPath,t,{encoding:`utf-8`})}async deleteFile(e){try{let t=e.fsPath,n=b(t,{throwIfNoEntry:!1});return n?.isFile()||n?.isSymbolicLink()?(await x(t),B.debug(`deleted file {path}`,{path:t}),!0):(B.warn(`deleteFile failed: {path} does not exist, or is not a file`,{path:t}),!1)}catch(t){B.warn(`Failed to delete file ${e.fsPath}`,{error:t})}return!1}};export{z as n,I as r,WithFileSystem as t};
@@ -0,0 +1 @@
1
+ import"./utils.mjs";import"./module.mjs";
@@ -1,7 +1,7 @@
1
1
  import { h as Locate, n as ChangeView, o as DidRequestOpenViewNotification } from "./protocol.mjs";
2
+ import { DefaultWeakMap, MultiMap } from "@likec4/core/utils";
2
3
  import * as langium from "langium";
3
4
  import { AstNode, AstNodeDescription, AsyncDisposable, BuildOptions, Cancellation, CstNode, DefaultAstNodeDescriptionProvider, DefaultDocumentValidator, DefaultIndexManager, DefaultLangiumDocuments, DefaultNameProvider, DefaultScopeComputation, DefaultScopeProvider, DefaultValueConverter, DefaultWorkspaceManager, DiagnosticInfo, Disposable, FileSelector, FileSystemNode, FileSystemProvider, GrammarAST, JSDocDocumentationProvider, LangiumDocument, LangiumDocumentFactory, MaybePromise, Module, PrecomputedScopes, Reference, ReferenceDescription, ReferenceInfo, Scope, SimpleCache, Stream, URI, ValidationOptions, ValueType, WorkspaceCache } from "langium";
4
- import { DefaultWeakMap, MultiMap as MultiMap$1 } from "@likec4/core/utils";
5
5
  import * as c4 from "@likec4/core";
6
6
  import { ComputedView, DiagramView, LayoutType, LayoutedView, NonEmptyArray, NonEmptyReadonlyArray, ProjectId, Tag, UnknownComputed, UnknownLayouted, UnknownParsed, ViewChange, ViewId } from "@likec4/core";
7
7
  import { CompletionItemKind, Diagnostic, DocumentSymbol, FormattingOptions, Hover, Location, Range, SymbolKind, TextEdit } from "vscode-languageserver-types";
@@ -41,6 +41,7 @@ declare class LikeC4DocumentationProvider extends JSDocDocumentationProvider {
41
41
  }
42
42
  //#endregion
43
43
  //#region src/workspace/ProjectsManager.d.ts
44
+ type NormalizedUri = Tagged<string, 'NormalizedUri'>;
44
45
  type DocOrUri = LangiumDocument | string | URI;
45
46
  /**
46
47
  * A tagged string that represents a project folder URI (with trailing slash).
@@ -91,6 +92,11 @@ declare class ProjectsManager {
91
92
  */
92
93
  static readonly DefaultProjectId: ProjectId$1;
93
94
  constructor(services: LikeC4SharedServices);
95
+ /**
96
+ * Checks if a document is excluded by workspace-level patterns.
97
+ * These patterns come from VS Code settings and take precedence over project-level excludes.
98
+ */
99
+ isExcludedByWorkspace(uri: NormalizedUri | URI): boolean;
94
100
  /**
95
101
  * Updates the workspace-level exclude patterns from VS Code settings.
96
102
  * Called during initial server startup; dynamic changes restart the server.
@@ -111,7 +117,7 @@ declare class ProjectsManager {
111
117
  /**
112
118
  * Returns all projects that overlap with the specified folder (is parent or child)
113
119
  */
114
- findOverlaped(folder: URI | string): ProjectData[];
120
+ findOverlaped(folder: URI | string): ReadonlyArray<ProjectData>;
115
121
  /**
116
122
  * Validates and ensures the project ID.
117
123
  * If no project ID is specified, returns default project ID
@@ -119,14 +125,22 @@ declare class ProjectsManager {
119
125
  */
120
126
  ensureProjectId(projectId?: ProjectId$1 | undefined): ProjectId$1;
121
127
  /**
122
- * Validates and ensures the project.
128
+ * Validates and ensures the project data.
129
+ * If projectId is not specified, returns default project
130
+ *
131
+ * If there are multiple projects and default project is not set, throws an error
132
+ *
133
+ * @see ensureProjectId - to validate project ID only
123
134
  */
124
135
  ensureProject(projectId?: ProjectId$1 | undefined): ProjectData;
125
136
  hasMultipleProjects(): boolean;
126
137
  /**
127
- * Checks if the specified document should be excluded from processing.
138
+ * Checks if given document (or URI) must be excluded from processing.
128
139
  */
129
140
  isExcluded(document: DocOrUri): boolean;
141
+ /**
142
+ * Checks if given document (or URI) must be excluded in the context of the project.
143
+ */
130
144
  isExcluded(projectId: ProjectId$1, document: DocOrUri): boolean;
131
145
  /**
132
146
  * Checks if the specified document is included by the project:
@@ -148,6 +162,16 @@ declare class ProjectsManager {
148
162
  * If the document does not belong to any project, returns the default project ID.
149
163
  */
150
164
  ownerProjectId(document: LangiumDocument | URI | string): ProjectId$1;
165
+ /**
166
+ * Returns path to the document relative to the project folder.
167
+ * If the document does not belong to any project, returns the document URI as string.
168
+ */
169
+ relativePath(document: LangiumDocument | URI | string): string;
170
+ /**
171
+ * Returns true if the manager is currently initializing or reloading projects.
172
+ * This is used to prevent duplicate reload operations.
173
+ */
174
+ protected get isInitiatingOrReloading(): boolean;
151
175
  reloadProjects(cancelToken?: Cancellation.CancellationToken): Promise<void>;
152
176
  protected _reloadProjects(cancelToken?: Cancellation.CancellationToken): Promise<void>;
153
177
  protected uniqueProjectId(name: string): ProjectId$1;
@@ -191,7 +215,8 @@ interface FileSystemProvider$1 extends FileSystemProvider {
191
215
  */
192
216
  loadProjectConfig(filepath: URI): Promise<LikeC4ProjectConfig>;
193
217
  /**
194
- * Reads the directory information for the given URI.
218
+ * Reads the directory and returns LikeC4 files.
219
+ *
195
220
  * @param options.recursive If true, recursively reads the directory,
196
221
  * @param options.maxDepth Maximum depth to traverse when recursive is true (default: Infinity)
197
222
  */
@@ -202,7 +227,7 @@ interface FileSystemProvider$1 extends FileSystemProvider {
202
227
  /**
203
228
  * Finds all files in the given directory, matching the given filter.
204
229
  */
205
- scanDirectory(directory: URI, filter: (filepath: string) => boolean): Promise<FileNode[]>;
230
+ scanDirectory(directory: URI, filter: (filepath: string, isDirectory: boolean) => boolean): Promise<FileNode[]>;
206
231
  /**
207
232
  * Writes the content to the file system.
208
233
  * Used by manual layouts.
@@ -386,7 +411,7 @@ type FqnReferenceable = Element | ExtendDeployment | ExtendElement | Referenceab
386
411
  declare const FqnReferenceable = "FqnReferenceable";
387
412
  type IconId = string;
388
413
  type IconPositionValue = 'bottom' | 'left' | 'right' | 'top';
389
- type Id = 'deployment' | 'element' | 'group' | 'instance' | 'model' | 'node' | ArrowType | DynamicViewDisplayVariantValue | ElementShape | IconPositionValue | LineOptions | Participant | RankValue | SizeValue | ThemeColor | string;
414
+ type Id = 'deployment' | 'element' | 'group' | 'instance' | 'model' | 'node' | 'relationship' | ArrowType | DynamicViewDisplayVariantValue | ElementShape | IconPositionValue | LineOptions | Participant | RankValue | SizeValue | ThemeColor | string;
390
415
  type LikeC4View = DeploymentView | DynamicView | ElementView;
391
416
  declare const LikeC4View = "LikeC4View";
392
417
  type LineOptions = 'dashed' | 'dotted' | 'solid';
@@ -1798,7 +1823,7 @@ interface LikeC4DocumentProps {
1798
1823
  c4Views?: ParsedAstView[];
1799
1824
  c4Deployments?: ParsedAstDeployment[];
1800
1825
  c4DeploymentRelations?: ParsedAstDeploymentRelation[];
1801
- c4Imports?: MultiMap$1<c4.ProjectId, c4.Fqn, Set<c4.Fqn>>;
1826
+ c4Imports?: MultiMap<c4.ProjectId, c4.Fqn, Set<c4.Fqn>>;
1802
1827
  }
1803
1828
  type LikeC4GrammarDocument = Omit<LangiumDocument<LikeC4Grammar>, 'diagnostics'>;
1804
1829
  interface LikeC4LangiumDocument extends LikeC4GrammarDocument, LikeC4DocumentProps {
@@ -1878,20 +1903,24 @@ declare class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
1878
1903
  initialBuildOptions: BuildOptions;
1879
1904
  constructor(services: LikeC4SharedServices);
1880
1905
  /**
1881
- * First load all project config files, then load all documents in the workspace.
1906
+ * Whether the workspace is ready, use {@link ready} promise to wait for it
1882
1907
  */
1883
- protected performStartup(folders: WorkspaceFolder[]): Promise<LangiumDocument[]>;
1908
+ get isReady(): boolean;
1884
1909
  /**
1885
- * Read workspace exclude patterns from configuration before workspace scan.
1886
- * Uses a timeout fallback for third-party IDEs that may not support workspace/configuration.
1910
+ * First load all project config files, then load all documents in the workspace.
1887
1911
  */
1888
- private readExcludeConfig;
1912
+ protected performStartup(folders: WorkspaceFolder[]): Promise<LangiumDocument[]>;
1889
1913
  /**
1890
1914
  * Load all additional documents that shall be visible in the context of the given workspace
1891
1915
  * folders and add them to the collector. This can be used to include built-in libraries of
1892
1916
  * your language, which can be either loaded from provided files or constructed in memory.
1893
1917
  */
1894
1918
  protected loadAdditionalDocuments(folders: WorkspaceFolder[], collector: (document: LangiumDocument) => void): Promise<void>;
1919
+ /**
1920
+ * Traverse the file system folder identified by the given URI and its subfolders. All
1921
+ * contained files that match the file extensions are added to the collector.
1922
+ */
1923
+ protected traverseFolder(workspaceFolder: WorkspaceFolder, folderPath: URI$2, selector: FileSelector, collector: (document: LangiumDocument) => void): Promise<void>;
1895
1924
  /**
1896
1925
  * Determine whether the given folder entry shall be included while indexing the workspace.
1897
1926
  */
@@ -1908,6 +1937,11 @@ declare class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
1908
1937
  * Register a listener to be called when caches are force cleaned
1909
1938
  */
1910
1939
  onForceCleanCache(listener: () => void): Disposable;
1940
+ /**
1941
+ * Read workspace exclude patterns from configuration before workspace scan.
1942
+ * Uses a timeout fallback for third-party IDEs that may not support workspace/configuration.
1943
+ */
1944
+ private readExcludeConfig;
1911
1945
  }
1912
1946
  //#endregion
1913
1947
  //#region src/references/scope-provider.d.ts
@@ -2005,17 +2039,17 @@ declare class DocumentFqnIndex {
2005
2039
  * direct children of elements
2006
2040
  */
2007
2041
 
2008
- _children: MultiMap$1<Fqn$1, AstNodeDescriptionWithFqn>,
2042
+ _children: MultiMap<Fqn$1, AstNodeDescriptionWithFqn>,
2009
2043
  /**
2010
2044
  * All descendants of an element (unique by name)
2011
2045
  */
2012
2046
 
2013
- _descendants: MultiMap$1<Fqn$1, AstNodeDescriptionWithFqn>,
2047
+ _descendants: MultiMap<Fqn$1, AstNodeDescriptionWithFqn>,
2014
2048
  /**
2015
2049
  * All elements by FQN
2016
2050
  */
2017
2051
 
2018
- _byfqn: MultiMap$1<Fqn$1, AstNodeDescriptionWithFqn>, projectId: ProjectId$1);
2052
+ _byfqn: MultiMap<Fqn$1, AstNodeDescriptionWithFqn>, projectId: ProjectId$1);
2019
2053
  rootElements(): readonly AstNodeDescriptionWithFqn[];
2020
2054
  byFqn(fqn: Fqn$1): readonly AstNodeDescriptionWithFqn[];
2021
2055
  children(parent: Fqn$1): readonly AstNodeDescriptionWithFqn[];
@@ -2042,7 +2076,7 @@ declare class MergedSpecification {
2042
2076
  readonly specs: Omit<ParsedAstSpecification, 'tags'>;
2043
2077
  readonly tags: Readonly<Record<c4.Tag, c4.TagSpecification>>;
2044
2078
  readonly globals: c4.ModelGlobals;
2045
- readonly imports: MultiMap$1<c4.ProjectId, c4.Fqn, Set<c4.Fqn>>;
2079
+ readonly imports: MultiMap<c4.ProjectId, c4.Fqn, Set<c4.Fqn>>;
2046
2080
  readonly projectId: c4.ProjectId | undefined;
2047
2081
  readonly inferTechFromIcon: boolean;
2048
2082
  constructor(docs: ReadonlyArray<ParsedLikeC4LangiumDocument>, opts?: {
@@ -2379,6 +2413,10 @@ declare class BaseParser {
2379
2413
  readonly doc: ParsedLikeC4LangiumDocument;
2380
2414
  readonly project: Project;
2381
2415
  isValid: IsValidFn;
2416
+ /** Full URI of the document */
2417
+ docUri: string;
2418
+ /** Path of the document relative to the project root */
2419
+ docPathInProject: string;
2382
2420
  constructor(services: LikeC4Services, doc: ParsedLikeC4LangiumDocument, project: Project);
2383
2421
  logError(error: unknown, astNode?: AstNode | Reference<AstNode>, level?: ParserLevel): void;
2384
2422
  tryParse<N extends AstNode, T>(level: ParserLevel, node: N | undefined, fn: (node: NoInfer<N>) => T | undefined): T | undefined;
@@ -2484,6 +2522,8 @@ declare const DocumentParserFromMixins: {
2484
2522
  parseInlineKindCondition(astNode: OutgoingRelationExpr): ProjectId | null;
2485
2523
  wrapInWhere(expr: ProjectId, condition: ProjectId | null): ProjectId;
2486
2524
  isValid: IsValidFn;
2525
+ docUri: string;
2526
+ docPathInProject: string;
2487
2527
  readonly services: LikeC4Services;
2488
2528
  readonly doc: ParsedLikeC4LangiumDocument;
2489
2529
  readonly project: Project;
@@ -2587,6 +2627,8 @@ declare const DocumentParserFromMixins: {
2587
2627
  parseInlineKindCondition(astNode: OutgoingRelationExpr): ProjectId | null;
2588
2628
  wrapInWhere(expr: ProjectId, condition: ProjectId | null): ProjectId;
2589
2629
  isValid: IsValidFn;
2630
+ docUri: string;
2631
+ docPathInProject: string;
2590
2632
  readonly services: LikeC4Services;
2591
2633
  readonly doc: ParsedLikeC4LangiumDocument;
2592
2634
  readonly project: Project;
@@ -2645,6 +2687,8 @@ declare const DocumentParserFromMixins: {
2645
2687
  parseElementSpecificationNode(specAst: SpecificationElementKind): {};
2646
2688
  parseElementSpecificationNode(specAst: SpecificationDeploymentNodeKind): {};
2647
2689
  isValid: IsValidFn;
2690
+ docUri: string;
2691
+ docPathInProject: string;
2648
2692
  readonly services: LikeC4Services;
2649
2693
  readonly doc: ParsedLikeC4LangiumDocument;
2650
2694
  readonly project: Project;
@@ -2718,6 +2762,8 @@ declare const DocumentParserFromMixins: {
2718
2762
  parseInlineKindCondition(astNode: OutgoingRelationExpr): ProjectId | null;
2719
2763
  wrapInWhere(expr: ProjectId, condition: ProjectId | null): ProjectId;
2720
2764
  isValid: IsValidFn;
2765
+ docUri: string;
2766
+ docPathInProject: string;
2721
2767
  readonly services: LikeC4Services;
2722
2768
  readonly doc: ParsedLikeC4LangiumDocument;
2723
2769
  readonly project: Project;
@@ -2784,6 +2830,8 @@ declare const DocumentParserFromMixins: {
2784
2830
  parseInlineKindCondition(astNode: OutgoingRelationExpr): ProjectId | null;
2785
2831
  wrapInWhere(expr: ProjectId, condition: ProjectId | null): ProjectId;
2786
2832
  isValid: IsValidFn;
2833
+ docUri: string;
2834
+ docPathInProject: string;
2787
2835
  readonly services: LikeC4Services;
2788
2836
  readonly doc: ParsedLikeC4LangiumDocument;
2789
2837
  readonly project: Project;
@@ -2858,6 +2906,8 @@ declare const DocumentParserFromMixins: {
2858
2906
  parseInlineKindCondition(astNode: OutgoingRelationExpr): ProjectId | null;
2859
2907
  wrapInWhere(expr: ProjectId, condition: ProjectId | null): ProjectId;
2860
2908
  isValid: IsValidFn;
2909
+ docUri: string;
2910
+ docPathInProject: string;
2861
2911
  readonly services: LikeC4Services;
2862
2912
  readonly doc: ParsedLikeC4LangiumDocument;
2863
2913
  readonly project: Project;
@@ -2926,6 +2976,8 @@ declare const DocumentParserFromMixins: {
2926
2976
  parseInlineKindCondition(astNode: OutgoingRelationExpr): ProjectId | null;
2927
2977
  wrapInWhere(expr: ProjectId, condition: ProjectId | null): ProjectId;
2928
2978
  isValid: IsValidFn;
2979
+ docUri: string;
2980
+ docPathInProject: string;
2929
2981
  readonly services: LikeC4Services;
2930
2982
  readonly doc: ParsedLikeC4LangiumDocument;
2931
2983
  readonly project: Project;
@@ -2972,6 +3024,8 @@ declare const DocumentParserFromMixins: {
2972
3024
  new (...args: any[]): {
2973
3025
  parseImports(): void;
2974
3026
  isValid: IsValidFn;
3027
+ docUri: string;
3028
+ docPathInProject: string;
2975
3029
  readonly services: LikeC4Services;
2976
3030
  readonly doc: ParsedLikeC4LangiumDocument;
2977
3031
  readonly project: Project;
@@ -3034,6 +3088,8 @@ declare const DocumentParserFromMixins: {
3034
3088
  parseInlineKindCondition(astNode: OutgoingRelationExpr): ProjectId | null;
3035
3089
  wrapInWhere(expr: ProjectId, condition: ProjectId | null): ProjectId;
3036
3090
  isValid: IsValidFn;
3091
+ docUri: string;
3092
+ docPathInProject: string;
3037
3093
  readonly services: LikeC4Services;
3038
3094
  readonly doc: ParsedLikeC4LangiumDocument;
3039
3095
  readonly project: Project;
@@ -3109,6 +3165,7 @@ declare class LikeC4ModelChanges {
3109
3165
  modifiedRange: Range;
3110
3166
  edits: TextEdit[];
3111
3167
  };
3168
+ protected applyTextEdits(doc: ParsedLikeC4LangiumDocument, edits: TextEdit[]): Promise<boolean>;
3112
3169
  }
3113
3170
  //#endregion
3114
3171
  //#region src/LikeC4LanguageServices.d.ts
@@ -3116,6 +3173,7 @@ interface LikeC4LanguageServices {
3116
3173
  readonly views: LikeC4Views;
3117
3174
  readonly builder: LikeC4ModelBuilder;
3118
3175
  readonly workspaceUri: URI;
3176
+ readonly workspacePath: string;
3119
3177
  readonly projectsManager: ProjectsManager;
3120
3178
  readonly editor: LikeC4ModelChanges;
3121
3179
  /**