text: import * as colorMode from './colorMode.js';
  
  const base = FOS.extensionBase + 'editor/';
  const monacoBase = __MONACO_BASE__; // gets replaced during the build
  
  const PATHS = {
      loaderJs: monacoBase + 'min/vs/loader.js',
      vsBaseMin: monacoBase + 'min/',
      vsBase: monacoBase + 'min/vs',
      monaco: 'vs/editor/editor.main',
      monacoDefaultWorker: monacoBase + 'min/vs/base/worker/workerMain.js',
      vsctm: base + 'third-party/vscode-textmate/main.js',
      vscog: base + 'third-party/vscode-oniguruma/main.js',
      onigWasm: base + 'third-party/vscode-oniguruma/onig.wasm',
      configurationsDir: base + 'assets/monaco/configurations/',
      grammarsDir: base + 'assets/monaco/grammars/',
      tmThemes: base + 'assets/monaco/tmThemes.js'
  };
  
  let tmThemes;
  let registry;
  
  function getTmTheme(themeName) {
      if (themeName == 'vs') {
          return tmThemes.tmVs;
      } else if (themeName == 'vs-dark') {
          return tmThemes.tmVsDark;
      }
  }
  
  async function setup(monaco, vsctm, oniguruma, themes, initialTheme, resolve) {
  
      // exposing globally so as other functions need to access the themes
      tmThemes = themes;
  
      // the encodedTokensColors were generated by calling registry.getColorMap()
      monaco.editor.defineTheme('vs', {
          "inherit": false,
          "base": "vs",
          "rules": [],
          "encodedTokensColors": ["#000000", "#FFFFFF", "#008000", "#0000FF", "#811F3F", "#EE0000", "#098658", "#0451A5", "#267F99", "#795E26", "#AF00DB", "#800000", "#001080", "#FF0000", "#CD3131", "#D16969", "#000080", "#A31515", "#800080", "#316BCD", "#CD9731", "#0070C1"]
      });
      monaco.editor.defineTheme('vs-dark', {
          "inherit": false,
          "base": "vs-dark",
          "rules": [],
          "encodedTokensColors": ["#D4D4D4", "#1E1E1E", "#6A9955", "#569CD6", "#D16969", "#D7BA7D", "#B5CEA8", "#CE9178", "#646695", "#4EC9B0", "#DCDCAA", "#C8C8C8", "#C586C0", "#9CDCFE", "#000080", "#F44747", "#6796E6", "#808080", "#B267E6", "#CD9731", "#4FC1FF"]
      });
  
      // https://github.com/microsoft/monaco-editor-webpack-plugin/issues/42
      window.MonacoEnvironment = {
          getWorkerUrl: function(workerId, label) {
              return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
                  self.MonacoEnvironment = {
                      baseUrl: '${PATHS.vsBaseMin}'
                  };
                  importScripts('${PATHS.monacoDefaultWorker}');`
              )}`;
          }
      };
  
      // beginning of tm grammar logic
  
      const languages = [{
          id: 'typescript',
          extensions: ['.ts'],
          aliases: ['TypeScript', 'ts']
      }, {
          id: 'javascript',
          extensions: ['.js'],
          aliases: ['JavaScript', 'js']
      }, {
          id: 'css',
          extensions: ['.css'],
          aliases: ['CSS', 'css']
      }, {
          id: 'less',
          extensions: ['.less'],
          aliases: ['LESS', 'less']
      }, {
          id: 'json',
          extensions: ['.json'],
          aliases: ['JSON', 'json']
      }, {
          id: 'html',
          extensions: ['.html'],
          aliases: ['HTML', 'html']
      }, {
          id: 'xml',
          extensions: ['.xml','.svg'],
          aliases: ['XML', 'xml']
      }
      ];
  
      // the scopeNames can be found in the tmLanguage.json file
      const grammars = {
          'source.js': {
              language: 'javascript',
              path: 'JavaScript.tmLanguage.json'
          },
          'source.css': {
              language: 'css',
              path: 'css.tmLanguage.json'
          },
          'source.css.less': {
              language: 'less',
              path: 'less.tmLanguage.json'
          },
          'source.ts': {
              language: 'typescript',
              path: 'TypeScript.tmLanguage.json'
          },
          'source.json': {
              language: 'json',
              path: 'JSON.tmLanguage.json'
          },
          'text.html.basic': {
              language: 'html',
              path: 'html.tmLanguage.json'
          },
          'text.xml': {
              language: 'xml',
              path: 'xml.tmLanguage.json'
          }
      };
  
      async function getTokensProviderForLanguage(language) {
          let sn = null;
  
          for (const [scopeName, grammar] of Object.entries(grammars)) {
              if (grammar.language === language) {
                  sn = scopeName;
              }
          }
  
          const grammar = await registry.loadGrammarWithConfiguration(sn, monaco.languages.getEncodedLanguageId(language), {});
  
          return {
              getInitialState: function () {
                  return vsctm.INITIAL;
              },
              tokenizeEncoded: function (line, state) {
                  const result = grammar.tokenizeLine2(line, state);
                  return {
                      tokens: result.tokens,
                      endState: result.ruleStack
                  };
              }
          };
      }
  
      async function fetchConfiguration(language) {
          const response = await fetch(PATHS.configurationsDir + language + '.json');
          return await response.json();
      };
  
      // the json language is special as it comes with its own tokenizer
      // disabling it here to use the TM one
      monaco.languages.json.jsonDefaults.modeConfiguration.tokens = false;
  
      // loading oniguruma
      const response = await fetch(PATHS.onigWasm);
      const contentType = response.headers.get('content-type');
      if (contentType === 'application/wasm') {
          await oniguruma.loadWASM(response);
      } else {
          await oniguruma.loadWASM(await response.arrayBuffer());
      }
  
      registry = new vsctm.Registry({
          onigLib: Promise.resolve({
              createOnigScanner: (sources) => new oniguruma.OnigScanner(sources),
              createOnigString: (str) => new oniguruma.OnigString(str)
          }),
          loadGrammar: async (scopeName) => {
              const path = grammars[scopeName].path;
              const response = await fetch(PATHS.grammarsDir + path);
              const grammar = await response.text();
  
              // The path must be passed as well so parseRawGrammar can distinguish between json and plist
              return vsctm.parseRawGrammar(grammar, path);
          },
          getInjections: (scopeName) => {
              return grammars[scopeName].injections;
          },
          // Note that nothing will display without the theme!
          theme: getTmTheme(initialTheme)
      });
  
      // We have to register all of the languages with Monaco synchronously before we can configure them.
      for (let i = 0; i < languages.length; i++) {
          const language = languages[i];
          const languageId = language.id;
  
          monaco.languages.register(language);
  
          // Lazy-load the tokens provider and configuration data.
          monaco.languages.onLanguage(languageId, async () => {
              const [tokensProvider, configuration] = await Promise.all([
                  getTokensProviderForLanguage(languageId),
                  fetchConfiguration(languageId),
              ]);
  
              monaco.languages.setTokensProvider(languageId, tokensProvider);
              monaco.languages.setLanguageConfiguration(languageId, configuration);
          });
      }
  
      // end of tm grammar logic
  
      resolve();
  }
  
  // this function should be invoked only once
  // it sets up the entire monaco environament and resolves the promise it returns when done
  export function setupMonaco() {
  
      return new Promise(resolve => {
  
          const requirejsPromise = window.require == undefined
              ? FOS.util.injectScript(PATHS.loaderJs)
              : Promise.resolve();
  
          requirejsPromise.then(() => {
              require.config({ paths: { 'vs': PATHS.vsBase } });
  
              require([PATHS.monaco, PATHS.vsctm, PATHS.vscog, PATHS.tmThemes], function (monaco, textmate, oniguruma, themes) {
                  setup(monaco, textmate, oniguruma, themes, getAppropriateMonacoTheme(), resolve);
              });
          });
      });
  }
  
  // expects vs or vs-dark
  function setTheme(theme) {
      if(window.monaco && registry){
          registry.setTheme(getTmTheme(theme));
          monaco.editor.setTheme(theme);
      }
  }
  
  // returns vs or vs-dark according to the current preference
  export function getAppropriateMonacoTheme(){
      let theme = FOS.util.getPreference(FOS.util.PREFERENCES.theme);
  
      if(theme == 'automatic' || !theme){
          theme = colorMode.getColorModeBinary() == 'dark' ? 'vs-dark' : 'vs';
      }
  
      return theme;
  }
  
  export function resetTheme(){
      setTheme(getAppropriateMonacoTheme())
  }
  
  document.addEventListener('fosThemeChange', function () {
      resetTheme();
  });