import { TextDocument } from "../vscode-languageserver-textdocument/lib-esm-main.js";
import { stream } from "./lib-utils-stream.js";
import "./lib-utils-uri-utils.js";
import { c as cancellationExports } from "../../_virtual/cancellation.js";
import { URI } from "../vscode-uri/lib-esm-index.js";
var DocumentState;
(function(DocumentState2) {
  DocumentState2[DocumentState2["Changed"] = 0] = "Changed";
  DocumentState2[DocumentState2["Parsed"] = 1] = "Parsed";
  DocumentState2[DocumentState2["IndexedContent"] = 2] = "IndexedContent";
  DocumentState2[DocumentState2["ComputedScopes"] = 3] = "ComputedScopes";
  DocumentState2[DocumentState2["Linked"] = 4] = "Linked";
  DocumentState2[DocumentState2["IndexedReferences"] = 5] = "IndexedReferences";
  DocumentState2[DocumentState2["Validated"] = 6] = "Validated";
})(DocumentState || (DocumentState = {}));
class DefaultLangiumDocumentFactory {
  constructor(services) {
    this.serviceRegistry = services.ServiceRegistry;
    this.textDocuments = services.workspace.TextDocuments;
    this.fileSystemProvider = services.workspace.FileSystemProvider;
  }
  async fromUri(uri, cancellationToken = cancellationExports.CancellationToken.None) {
    const content = await this.fileSystemProvider.readFile(uri);
    return this.createAsync(uri, content, cancellationToken);
  }
  fromTextDocument(textDocument, uri, token) {
    uri = uri !== null && uri !== void 0 ? uri : URI.parse(textDocument.uri);
    if (cancellationExports.CancellationToken.is(token)) {
      return this.createAsync(uri, textDocument, token);
    } else {
      return this.create(uri, textDocument, token);
    }
  }
  fromString(text, uri, token) {
    if (cancellationExports.CancellationToken.is(token)) {
      return this.createAsync(uri, text, token);
    } else {
      return this.create(uri, text, token);
    }
  }
  fromModel(model, uri) {
    return this.create(uri, { $model: model });
  }
  create(uri, content, options) {
    if (typeof content === "string") {
      const parseResult = this.parse(uri, content, options);
      return this.createLangiumDocument(parseResult, uri, void 0, content);
    } else if ("$model" in content) {
      const parseResult = { value: content.$model, parserErrors: [], lexerErrors: [] };
      return this.createLangiumDocument(parseResult, uri);
    } else {
      const parseResult = this.parse(uri, content.getText(), options);
      return this.createLangiumDocument(parseResult, uri, content);
    }
  }
  async createAsync(uri, content, cancelToken) {
    if (typeof content === "string") {
      const parseResult = await this.parseAsync(uri, content, cancelToken);
      return this.createLangiumDocument(parseResult, uri, void 0, content);
    } else {
      const parseResult = await this.parseAsync(uri, content.getText(), cancelToken);
      return this.createLangiumDocument(parseResult, uri, content);
    }
  }
  /**
   * Create a LangiumDocument from a given parse result.
   *
   * A TextDocument is created on demand if it is not provided as argument here. Usually this
   * should not be necessary because the main purpose of the TextDocument is to convert between
   * text ranges and offsets, which is done solely in LSP request handling.
   *
   * With the introduction of {@link update} below this method is supposed to be mainly called
   * during workspace initialization and on addition/recognition of new files, while changes in
   * existing documents are processed via {@link update}.
   */
  createLangiumDocument(parseResult, uri, textDocument, text) {
    let document;
    if (textDocument) {
      document = {
        parseResult,
        uri,
        state: DocumentState.Parsed,
        references: [],
        textDocument
      };
    } else {
      const textDocumentGetter = this.createTextDocumentGetter(uri, text);
      document = {
        parseResult,
        uri,
        state: DocumentState.Parsed,
        references: [],
        get textDocument() {
          return textDocumentGetter();
        }
      };
    }
    parseResult.value.$document = document;
    return document;
  }
  async update(document, cancellationToken) {
    var _a, _b;
    const oldText = (_a = document.parseResult.value.$cstNode) === null || _a === void 0 ? void 0 : _a.root.fullText;
    const textDocument = (_b = this.textDocuments) === null || _b === void 0 ? void 0 : _b.get(document.uri.toString());
    const text = textDocument ? textDocument.getText() : await this.fileSystemProvider.readFile(document.uri);
    if (textDocument) {
      Object.defineProperty(document, "textDocument", {
        value: textDocument
      });
    } else {
      const textDocumentGetter = this.createTextDocumentGetter(document.uri, text);
      Object.defineProperty(document, "textDocument", {
        get: textDocumentGetter
      });
    }
    if (oldText !== text) {
      document.parseResult = await this.parseAsync(document.uri, text, cancellationToken);
      document.parseResult.value.$document = document;
    }
    document.state = DocumentState.Parsed;
    return document;
  }
  parse(uri, text, options) {
    const services = this.serviceRegistry.getServices(uri);
    return services.parser.LangiumParser.parse(text, options);
  }
  parseAsync(uri, text, cancellationToken) {
    const services = this.serviceRegistry.getServices(uri);
    return services.parser.AsyncParser.parse(text, cancellationToken);
  }
  createTextDocumentGetter(uri, text) {
    const serviceRegistry = this.serviceRegistry;
    let textDoc = void 0;
    return () => {
      return textDoc !== null && textDoc !== void 0 ? textDoc : textDoc = TextDocument.create(uri.toString(), serviceRegistry.getServices(uri).LanguageMetaData.languageId, 0, text !== null && text !== void 0 ? text : "");
    };
  }
}
class DefaultLangiumDocuments {
  constructor(services) {
    this.documentMap = /* @__PURE__ */ new Map();
    this.langiumDocumentFactory = services.workspace.LangiumDocumentFactory;
    this.serviceRegistry = services.ServiceRegistry;
  }
  get all() {
    return stream(this.documentMap.values());
  }
  addDocument(document) {
    const uriString = document.uri.toString();
    if (this.documentMap.has(uriString)) {
      throw new Error(`A document with the URI '${uriString}' is already present.`);
    }
    this.documentMap.set(uriString, document);
  }
  getDocument(uri) {
    const uriString = uri.toString();
    return this.documentMap.get(uriString);
  }
  async getOrCreateDocument(uri, cancellationToken) {
    let document = this.getDocument(uri);
    if (document) {
      return document;
    }
    document = await this.langiumDocumentFactory.fromUri(uri, cancellationToken);
    this.addDocument(document);
    return document;
  }
  createDocument(uri, text, cancellationToken) {
    if (cancellationToken) {
      return this.langiumDocumentFactory.fromString(text, uri, cancellationToken).then((document) => {
        this.addDocument(document);
        return document;
      });
    } else {
      const document = this.langiumDocumentFactory.fromString(text, uri);
      this.addDocument(document);
      return document;
    }
  }
  hasDocument(uri) {
    return this.documentMap.has(uri.toString());
  }
  invalidateDocument(uri) {
    const uriString = uri.toString();
    const langiumDoc = this.documentMap.get(uriString);
    if (langiumDoc) {
      const linker = this.serviceRegistry.getServices(uri).references.Linker;
      linker.unlink(langiumDoc);
      langiumDoc.state = DocumentState.Changed;
      langiumDoc.precomputedScopes = void 0;
      langiumDoc.diagnostics = void 0;
    }
    return langiumDoc;
  }
  deleteDocument(uri) {
    const uriString = uri.toString();
    const langiumDoc = this.documentMap.get(uriString);
    if (langiumDoc) {
      langiumDoc.state = DocumentState.Changed;
      this.documentMap.delete(uriString);
    }
    return langiumDoc;
  }
}
export {
  DefaultLangiumDocumentFactory,
  DefaultLangiumDocuments,
  DocumentState,
  TextDocument
};
//# sourceMappingURL=lib-workspace-documents.js.map
