'use strict';

var obsidian = require('obsidian');

/******************************************************************************
Copyright (c) Microsoft Corporation.

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */

function __awaiter(thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
}

function getAllExpandersQuery(content) {
    let accum = [];
    for (var i = 0; i < content.length; i++) {
        const line = content[i];
        if (line === '```expander') {
            for (var e = 0; e < content.length - i; e++) {
                const nextline = content[i + e];
                if (nextline === '```') {
                    accum.push({
                        start: i,
                        end: i + e,
                        query: content[i + 1],
                        template: e > 2 ? content.slice(i + 2, i + e).join('\n') : ''
                    });
                    break;
                }
            }
        }
    }
    return accum;
}
function getClosestQuery(queries, lineNumber) {
    if (queries.length === 0) {
        return undefined;
    }
    return queries.reduce((a, b) => {
        return Math.abs(b.start - lineNumber) < Math.abs(a.start - lineNumber) ? b : a;
    });
}
function getLastLineToReplace(content, query, endline) {
    const lineFrom = query.end;
    for (var i = lineFrom + 1; i < content.length; i++) {
        if (content[i] === endline) {
            return i;
        }
    }
    return lineFrom + 1;
}
const pick = (obj, arr) => arr.reduce((acc, curr) => {
    return (curr in obj)
        ? Object.assign({}, acc, { [curr]: obj[curr] })
        : acc;
}, {});

// Functions for string processing
function splitByLines(content) {
    return content.split('\n');
}
function removeEmptyLines(s) {
    const lines = s.split('\n').map(e => e.trim());
    if (lines.length < 2) {
        return s;
    }
    else if (lines.indexOf('') === 0) {
        return removeEmptyLines(lines.slice(1).join('\n'));
    }
    return s;
}
function removeFrontMatter(s, lookEnding = false) {
    const lines = s.split('\n');
    if (lookEnding && lines.indexOf('---') === 0) {
        return lines.slice(1).join('\n');
    }
    else if (lookEnding) {
        return removeFrontMatter(lines.slice(1).join('\n'), true);
    }
    else if (lines.indexOf('---') === 0) {
        return removeFrontMatter(lines.slice(1).join('\n'), true);
    }
    return s;
}
function trimContent(content) {
    return removeFrontMatter(removeEmptyLines(content));
}

function getFrontMatter(file, plugin, s) {
    const { frontmatter = null } = plugin.app.metadataCache.getCache(file.path);
    if (frontmatter) {
        return frontmatter[s.split(':')[1]] || '';
    }
    return '';
}
function getFileInfo(plugin, file) {
    return __awaiter(this, void 0, void 0, function* () {
        const info = Object.assign({}, file, {
            content: file.extension === 'md' ? yield plugin.app.vault.cachedRead(file) : '',
            link: plugin.app.fileManager.generateMarkdownLink(file, file.name).replace(/^!/, '')
        }, plugin.app.metadataCache.getFileCache(file));
        return pick(info, [
            'basename',
            'content',
            'extension',
            'headings',
            'link', 'name',
            'path', 'sections', 'stat',
            'frontmatter',
            'links',
            'listItems'
        ]);
    });
}

function highlight(lineStart, lineEnd, matchStart, matchEnd, lineContent) {
    return [
        ...lineContent.slice(0, matchStart - lineStart),
        '==',
        ...lineContent.slice(matchStart - lineStart, (matchStart - lineStart) + (matchEnd - matchStart)),
        '==',
        ...lineContent.slice((matchStart - lineStart) + (matchEnd - matchStart)),
    ].join('');
}
const sequences = [
    {
        name: '\\$count',
        loop: true,
        format: (_p, _s, _content, _file, _d, index) => index ? String(index + 1) : String(1),
        desc: 'add index number to each produced file'
    },
    {
        name: '\\$filename',
        loop: true,
        format: (_p, _s, _content, file) => file.basename,
        desc: 'name of the founded file'
    },
    {
        name: '\\$link',
        loop: true,
        format: (p, _s, _content, file) => p.app.fileManager.generateMarkdownLink(file, file.path).replace('![[', '[['),
        desc: 'link based on Obsidian settings'
    },
    {
        name: '\\$lines:\\d+',
        loop: true,
        readContent: true,
        format: (p, s, content, _file) => {
            const digits = Number(s.split(':')[1]);
            return trimContent(content)
                .split('\n')
                .filter((_, i) => i < digits)
                .join('\n')
                .replace(new RegExp(p.config.lineEnding, 'g'), '');
        },
        desc: 'specified count of lines from the found file'
    },
    {
        name: '\\$characters:\\d+',
        loop: true,
        readContent: true,
        format: (p, s, content, _file) => {
            const digits = Number(s.split(':')[1]);
            return trimContent(content)
                .split('')
                .filter((_, i) => i < digits)
                .join('')
                .replace(new RegExp(p.config.lineEnding, 'g'), '');
        },
        desc: 'specified count of lines from the found file'
    },
    {
        name: '\\$frontmatter:[\\p\{L\}_-]+',
        loop: true,
        format: (p, s, _content, file) => getFrontMatter(file, p, s),
        desc: 'value from the frontmatter key in the found file'
    },
    {
        name: '\\$lines+',
        loop: true,
        readContent: true,
        format: (p, s, content, _file) => content.replace(new RegExp(p.config.lineEnding, 'g'), ''),
        desc: 'all content from the found file'
    },
    {
        name: '\\$ext',
        loop: true,
        format: (_p, s, content, file) => file.extension,
        desc: 'return file extension'
    },
    {
        name: '\\$created:format:date',
        loop: true,
        format: (_p, s, content, file) => String(new Date(file.stat.ctime).toISOString()).split('T')[0],
        desc: 'created time formatted'
    },
    {
        name: '\\$created:format:time',
        loop: true,
        format: (_p, s, content, file) => String(new Date(file.stat.ctime).toISOString()).split(/([.T])/)[2],
        desc: 'created time formatted'
    },
    {
        name: '\\$created:format',
        loop: true,
        format: (_p, s, content, file) => String(new Date(file.stat.ctime).toISOString()),
        desc: 'created time formatted'
    },
    {
        name: '\\$created',
        loop: true,
        format: (_p, s, content, file) => String(file.stat.ctime),
        desc: 'created time'
    },
    {
        name: '\\$size',
        loop: true,
        format: (_p, s, content, file) => String(file.stat.size),
        desc: 'size of the file'
    },
    {
        name: '\\$path',
        loop: true,
        format: (_p, s, content, file) => file.path,
        desc: 'path to the found file'
    },
    {
        name: '\\$parent',
        loop: true,
        format: (_p, s, content, file) => file.parent.name,
        desc: 'parent folder name'
    },
    {
        name: '^(.+|)\\$header:.+',
        loop: true,
        format: (p, s, content, file) => {
            var _a;
            const prefix = s.slice(0, s.indexOf('$'));
            const header = s.slice(s.indexOf('$')).replace('$header:', '').replace(/"/g, '');
            const neededLevel = header.split("#").length - 1;
            const neededTitle = header.replace(/^#+/g, '').trim();
            const metadata = p.app.metadataCache.getFileCache(file);
            return ((_a = metadata.headings) === null || _a === void 0 ? void 0 : _a.filter(e => {
                const tests = [
                    [neededTitle, e.heading.includes(neededTitle)],
                    [neededLevel, e.level === neededLevel]
                ].filter(e => e[0]);
                if (tests.length) {
                    return tests.map(e => e[1]).every(e => e === true);
                }
                return true;
            }).map(h => p.app.fileManager.generateMarkdownLink(file, file.basename, '#' + h.heading)).map(link => prefix + link).join('\n')) || '';
        },
        desc: 'headings from founded files. $header:## - return all level 2 headings. $header:Title - return all heading which match the string. Can be prepended like: - !$header:## to transclude the headings.'
    },
    {
        name: '^(.+|)\\$blocks',
        readContent: true,
        loop: true,
        format: (p, s, content, file) => {
            const prefix = s.slice(0, s.indexOf('$'));
            return content
                .split('\n')
                .filter(e => /\^\w+$/.test(e))
                .map(e => prefix + p.app.fileManager.generateMarkdownLink(file, file.basename, '#' + e.replace(/^.+?(\^\w+$)/, '$1')))
                .join('\n');
        },
        desc: 'block ids from the found files. Can be prepended.'
    },
    {
        name: '^(.+|)\\$match:header', loop: true, format: (p, s, content, file, results) => {
            var _a;
            const prefix = s.slice(0, s.indexOf('$'));
            const metadata = p.app.metadataCache.getFileCache(file);
            const headings = (_a = metadata.headings) === null || _a === void 0 ? void 0 : _a.filter(h => results.result.content.filter(c => h.position.end.offset < c[0]).some(e => e)).slice(-1);
            return headings
                .map(h => p.app.fileManager.generateMarkdownLink(file, file.basename, '#' + h.heading))
                .map(link => prefix + link)
                .join('\n') || '';
        }, desc: 'extract found selections'
    },
    {
        name: '^(.+|)\\$matchline(:(\\+|-|)\\d+:\\d+|:(\\+|-|)\\d+|)',
        loop: true,
        format: (_p, s, content, file, results) => {
            const prefix = s.slice(0, s.indexOf('$matchline'));
            const [keyword, context, limit] = s.slice(s.indexOf('$matchline')).split(':');
            const value = context || '';
            const limitValue = Number(limit);
            const isPlus = value.contains('+');
            const isMinus = value.contains('-');
            const isContext = !isPlus && !isMinus;
            const offset = Number(value.replace(/[+-]/, ''));
            const lines = results.content.split('\n');
            // Grab info about line content, index, text length and start/end character position
            const lineInfos = [];
            for (let i = 0; i < lines.length; i++) {
                const text = lines[i];
                if (i === 0) {
                    lineInfos.push({
                        num: 0,
                        start: 0,
                        end: text.length,
                        text
                    });
                    continue;
                }
                const start = lineInfos[i - 1].end + 1;
                lineInfos.push({
                    num: i,
                    start,
                    text,
                    end: text.length + start
                });
            }
            return results.result.content.map(([from, to]) => {
                const matchedLines = lineInfos
                    .filter(({ start, end }) => start <= from && end >= to)
                    .map((line) => {
                    return Object.assign(Object.assign({}, line), { text: highlight(line.start, line.end, from, to, line.text) });
                });
                const resultLines = [];
                for (const matchedLine of matchedLines) {
                    const prevLines = isMinus || isContext
                        ? lineInfos.filter(l => matchedLine.num - l.num > 0 && matchedLine.num - l.num < offset)
                        : [];
                    const nextLines = isPlus || isContext
                        ? lineInfos.filter(l => l.num - matchedLine.num > 0 && l.num - matchedLine.num < offset)
                        : [];
                    resultLines.push(...prevLines, matchedLine, ...nextLines);
                }
                return prefix + resultLines.map(e => e.text).join('\n');
            }).map(line => limitValue ? line.slice(0, limitValue) : line).join('\n');
        }, desc: 'extract line with matches'
    },
    {
        name: '^(.+|)\\$searchresult',
        loop: true,
        desc: '',
        format: (_p, s, content, file, results) => {
            const prefix = s.slice(0, s.indexOf('$searchresult'));
            return results.children.map(matchedFile => {
                return prefix + matchedFile.el.innerText;
            }).join('\n');
        }
    },
    {
        name: '^(.+|)\\$match', loop: true, format: (_p, s, content, file, results) => {
            if (!results.result.content) {
                console.warn('There is no content in results');
                return '';
            }
            function appendPrefix(prefix, line) {
                return prefix + line;
            }
            const prefixContent = s.slice(0, s.indexOf('$'));
            return results.result.content
                .map(([from, to]) => results.content.slice(from, to))
                .map(line => appendPrefix(prefixContent, line))
                .join('\n');
        }, desc: 'extract found selections'
    },
];

function extractFilesFromSearchResults(searchResults, currentFileName, excludeCurrent = true) {
    const files = Array.from(searchResults.keys());
    return excludeCurrent
        ? files.filter(file => file.basename !== currentFileName)
        : files;
}

var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};

function createCommonjsModule(fn, basedir, module) {
	return module = {
		path: basedir,
		exports: {},
		require: function (path, base) {
			return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
		}
	}, fn(module, module.exports), module.exports;
}

function commonjsRequire () {
	throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
}

var eta_min = createCommonjsModule(function (module, exports) {
!function(e,n){n(exports);}(commonjsGlobal,(function(e){function n(e){var t,r,i=new Error(e);return t=i,r=n.prototype,Object.setPrototypeOf?Object.setPrototypeOf(t,r):t.__proto__=r,i}function t(e,t,r){var i=t.slice(0,r).split(/\n/),a=i.length,o=i[a-1].length+1;throw n(e+=" at line "+a+" col "+o+":\n\n  "+t.split(/\n/)[a-1]+"\n  "+Array(o).join(" ")+"^")}n.prototype=Object.create(Error.prototype,{name:{value:"Eta Error",enumerable:!1}});var r=new Function("return this")().Promise;function i(e,n){for(var t in n)r=n,i=t,Object.prototype.hasOwnProperty.call(r,i)&&(e[t]=n[t]);var r,i;return e}function a(e,n,t,r){var i,a;return Array.isArray(n.autoTrim)?(i=n.autoTrim[1],a=n.autoTrim[0]):i=a=n.autoTrim,(t||!1===t)&&(i=t),(r||!1===r)&&(a=r),a||i?"slurp"===i&&"slurp"===a?e.trim():("_"===i||"slurp"===i?e=function(e){return String.prototype.trimLeft?e.trimLeft():e.replace(/^\s+/,"")}(e):"-"!==i&&"nl"!==i||(e=e.replace(/^(?:\r\n|\n|\r)/,"")),"_"===a||"slurp"===a?e=function(e){return String.prototype.trimRight?e.trimRight():e.replace(/\s+$/,"")}(e):"-"!==a&&"nl"!==a||(e=e.replace(/(?:\r\n|\n|\r)$/,"")),e):e}var o={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};function c(e){return o[e]}var s=/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})*}|(?!\${)[^\\`])*`/g,l=/'(?:\\[\s\w"'\\`]|[^\n\r'\\])*?'/g,u=/"(?:\\[\s\w"'\\`]|[^\n\r"\\])*?"/g;function p(e){return e.replace(/[.*+\-?^${}()|[\]\\]/g,"\\$&")}function f(e,n){var r=[],i=!1,o=0,c=n.parse;if(n.plugins)for(var f=0;f<n.plugins.length;f++){(T=n.plugins[f]).processTemplate&&(e=T.processTemplate(e,n));}function d(e,t){e&&(e=a(e,n,i,t))&&(e=e.replace(/\\|'/g,"\\$&").replace(/\r\n|\n|\r/g,"\\n"),r.push(e));}n.rmWhitespace&&(e=e.replace(/[\r\n]+/g,"\n").replace(/^\s+|\s+$/gm,"")),s.lastIndex=0,l.lastIndex=0,u.lastIndex=0;for(var g,h=[c.exec,c.interpolate,c.raw].reduce((function(e,n){return e&&n?e+"|"+p(n):n?p(n):e}),""),m=new RegExp("([^]*?)"+p(n.tags[0])+"(-|_)?\\s*("+h+")?\\s*","g"),v=new RegExp("'|\"|`|\\/\\*|(\\s*(-|_)?"+p(n.tags[1])+")","g");g=m.exec(e);){o=g[0].length+g.index;var y=g[1],x=g[2],_=g[3]||"";d(y,x),v.lastIndex=o;for(var w=void 0,b=!1;w=v.exec(e);){if(w[1]){var E=e.slice(o,w.index);m.lastIndex=o=v.lastIndex,i=w[2],b={t:_===c.exec?"e":_===c.raw?"r":_===c.interpolate?"i":"",val:E};break}var I=w[0];if("/*"===I){var R=e.indexOf("*/",v.lastIndex);-1===R&&t("unclosed comment",e,w.index),v.lastIndex=R;}else if("'"===I){l.lastIndex=w.index,l.exec(e)?v.lastIndex=l.lastIndex:t("unclosed string",e,w.index);}else if('"'===I){u.lastIndex=w.index,u.exec(e)?v.lastIndex=u.lastIndex:t("unclosed string",e,w.index);}else if("`"===I){s.lastIndex=w.index,s.exec(e)?v.lastIndex=s.lastIndex:t("unclosed string",e,w.index);}}b?r.push(b):t("unclosed tag",e,g.index+y.length);}if(d(e.slice(o,e.length),!1),n.plugins)for(f=0;f<n.plugins.length;f++){var T;(T=n.plugins[f]).processAST&&(r=T.processAST(r,n));}return r}function d(e,n){var t=f(e,n),r="var tR='',__l,__lP"+(n.include?",include=E.include.bind(E)":"")+(n.includeFile?",includeFile=E.includeFile.bind(E)":"")+"\nfunction layout(p,d){__l=p;__lP=d}\n"+(n.useWith?"with("+n.varName+"||{}){":"")+function(e,n){var t=0,r=e.length,i="";for(;t<r;t++){var a=e[t];if("string"==typeof a){i+="tR+='"+a+"'\n";}else {var o=a.t,c=a.val||"";"r"===o?(n.filter&&(c="E.filter("+c+")"),i+="tR+="+c+"\n"):"i"===o?(n.filter&&(c="E.filter("+c+")"),n.autoEscape&&(c="E.e("+c+")"),i+="tR+="+c+"\n"):"e"===o&&(i+=c+"\n");}}return i}(t,n)+(n.includeFile?"if(__l)tR="+(n.async?"await ":"")+"includeFile(__l,Object.assign("+n.varName+",{body:tR},__lP))\n":n.include?"if(__l)tR="+(n.async?"await ":"")+"include(__l,Object.assign("+n.varName+",{body:tR},__lP))\n":"")+"if(cb){cb(null,tR)} return tR"+(n.useWith?"}":"");if(n.plugins)for(var i=0;i<n.plugins.length;i++){var a=n.plugins[i];a.processFnString&&(r=a.processFnString(r,n));}return r}var g=new(function(){function e(e){this.cache=e;}return e.prototype.define=function(e,n){this.cache[e]=n;},e.prototype.get=function(e){return this.cache[e]},e.prototype.remove=function(e){delete this.cache[e];},e.prototype.reset=function(){this.cache={};},e.prototype.load=function(e){i(this.cache,e);},e}())({});var h={async:!1,autoEscape:!0,autoTrim:[!1,"nl"],cache:!1,e:function(e){var n=String(e);return /[&<>"']/.test(n)?n.replace(/[&<>"']/g,c):n},include:function(e,t){var r=this.templates.get(e);if(!r)throw n('Could not fetch template "'+e+'"');return r(t,this)},parse:{exec:"",interpolate:"=",raw:"~"},plugins:[],rmWhitespace:!1,tags:["<%","%>"],templates:g,useWith:!1,varName:"it"};function m(e,n){var t={};return i(t,h),n&&i(t,n),e&&i(t,e),t}function v(e,t){var r=m(t||{}),i=r.async?function(){try{return new Function("return (async function(){}).constructor")()}catch(e){throw e instanceof SyntaxError?n("This environment doesn't support async/await"):e}}():Function;try{return new i(r.varName,"E","cb",d(e,r))}catch(t){throw t instanceof SyntaxError?n("Bad template syntax\n\n"+t.message+"\n"+Array(t.message.length+1).join("=")+"\n"+d(e,r)+"\n"):t}}function y(e,n){if(n.cache&&n.name&&n.templates.get(n.name))return n.templates.get(n.name);var t="function"==typeof e?e:v(e,n);return n.cache&&n.name&&n.templates.define(n.name,t),t}function x(e,t,i,a){var o=m(i||{});if(!o.async)return y(e,o)(t,o);if(!a){if("function"==typeof r)return new r((function(n,r){try{n(y(e,o)(t,o));}catch(e){r(e);}}));throw n("Please provide a callback function, this env doesn't support Promises")}try{y(e,o)(t,o,a);}catch(e){return a(e)}}e.compile=v,e.compileToString=d,e.config=h,e.configure=function(e){return i(h,e)},e.defaultConfig=h,e.getConfig=m,e.parse=f,e.render=x,e.renderAsync=function(e,n,t,r){return x(e,n,Object.assign({},t,{async:!0}),r)},e.templates=g,Object.defineProperty(e,"__esModule",{value:!0});}));

});

class TextExpander extends obsidian.Plugin {
    constructor(app, plugin) {
        super(app, plugin);
        this.config = {
            autoExpand: false,
            defaultTemplate: '- $link',
            delay: 300,
            excludeCurrent: true,
            lineEnding: '<-->',
            prefixes: {
                header: '^',
                footer: '>'
            }
        };
        this.seqs = sequences;
        this.leftPanelInfo = {
            collapsed: false,
            tab: 0,
            text: ''
        };
        this.search = this.search.bind(this);
        this.init = this.init.bind(this);
        this.autoExpand = this.autoExpand.bind(this);
    }
    autoExpand() {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this.config.autoExpand) {
                return;
            }
            const activeLeaf = this.app.workspace.activeLeaf;
            if (!activeLeaf) {
                return;
            }
            const activeView = activeLeaf.view;
            const isAllowedView = activeView instanceof obsidian.MarkdownView;
            if (!isAllowedView) {
                return;
            }
            yield this.init(true);
        });
    }
    onload() {
        return __awaiter(this, void 0, void 0, function* () {
            this.addSettingTab(new SettingTab(this.app, this));
            this.registerMarkdownCodeBlockProcessor('expander', (source, el, ctx) => {
                el
                    .createDiv()
                    .createEl('button', { text: 'Run expand query' })
                    .addEventListener('click', this.init.bind(this, false, ctx.getSectionInfo(el).lineStart));
            });
            this.addCommand({
                id: 'editor-expand',
                name: 'expand',
                callback: this.init,
                hotkeys: []
            });
            this.addCommand({
                id: 'editor-expand-all',
                name: 'expand all',
                callback: () => this.init(true),
                hotkeys: []
            });
            this.app.workspace.on('file-open', this.autoExpand);
            const data = yield this.loadData();
            if (data) {
                this.config = Object.assign(Object.assign({}, this.config), data);
            }
        });
    }
    onunload() {
        console.log('unloading plugin');
        this.app.workspace.off('file-open', this.autoExpand);
    }
    saveSettings() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.saveData(this.config);
        });
    }
    init(proceedAllQueriesOnPage = false, lineToStart) {
        return __awaiter(this, void 0, void 0, function* () {
            const currentView = this.app.workspace.activeLeaf.view;
            // Is on editable view
            if (!(currentView instanceof obsidian.MarkdownView)) {
                return;
            }
            const cmDoc = this.cm = currentView.editor;
            const curNum = lineToStart || cmDoc.getCursor().line;
            const content = cmDoc.getValue();
            if (lineToStart) {
                cmDoc.setCursor(lineToStart ? lineToStart - 1 : 0);
            }
            const formatted = splitByLines(content);
            const findQueries = getAllExpandersQuery(formatted);
            const closestQuery = getClosestQuery(findQueries, curNum);
            if (proceedAllQueriesOnPage) {
                yield findQueries.reduce((promise, query, i) => promise.then(() => {
                    const newContent = splitByLines(cmDoc.getValue());
                    const updatedQueries = getAllExpandersQuery(newContent);
                    return this.runExpanderCodeBlock(updatedQueries[i], newContent, currentView);
                }), Promise.resolve());
            }
            else {
                yield this.runExpanderCodeBlock(closestQuery, formatted, currentView);
            }
        });
    }
    runExpanderCodeBlock(query, content, view) {
        return __awaiter(this, void 0, void 0, function* () {
            const { lineEnding, prefixes } = this.config;
            if (!query) {
                new Notification('Expand query not found');
                return Promise.resolve();
            }
            this.clearOldResultsInFile(content, query, lineEnding);
            const newContent = splitByLines(this.cm.getValue());
            if (query.query !== '') {
                this.search(query.query);
            }
            return yield this.runTemplateProcessing(query, getLastLineToReplace(newContent, query, this.config.lineEnding), prefixes, view);
        });
    }
    runTemplateProcessing(query, lastLine, prefixes, currentView) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            let currentFileName = '';
            const templateContent = query.template.split('\n');
            const { heading, footer, repeatableContent } = this.parseTemplate(prefixes, templateContent);
            if (currentView instanceof obsidian.FileView) {
                currentFileName = currentView.file.basename;
            }
            this.saveLeftPanelState();
            const searchResults = yield this.getFoundAfterDelay(query.query === '');
            const files = extractFilesFromSearchResults(searchResults, currentFileName, this.config.excludeCurrent);
            this.restoreLeftPanelState();
            currentView.editor.focus();
            const currentFileInfo = (currentView instanceof obsidian.FileView)
                ? yield getFileInfo(this, currentView.file)
                : {};
            const filesInfo = yield Promise.all(files.map(file => getFileInfo(this, file)));
            let changed;
            if (query.template.contains("<%")) {
                const templateToRender = repeatableContent.join('\n');
                const dataToRender = {
                    current: currentFileInfo,
                    files: filesInfo
                };
                changed = yield eta_min.render(templateToRender, dataToRender, { autoEscape: false });
                // changed = doT.template(templateToRender, {strip: false})(dataToRender)
            }
            else {
                changed = yield this.generateTemplateFromSequences(files, repeatableContent, searchResults);
            }
            let result = [
                heading,
                changed,
                footer,
                this.config.lineEnding
            ].filter(e => e).join('\n');
            // Do not paste generated content if used changed activeLeaf
            const viewBeforeReplace = this.app.workspace.activeLeaf.view;
            if (!(viewBeforeReplace instanceof obsidian.MarkdownView) || viewBeforeReplace.file.basename !== currentFileName) {
                return;
            }
            currentView.editor.replaceRange(result, { line: query.end + 1, ch: 0 }, { line: lastLine, ch: ((_a = this.cm.getLine(lastLine)) === null || _a === void 0 ? void 0 : _a.length) || 0 });
            return Promise.resolve();
        });
    }
    generateTemplateFromSequences(files, repeatableContent, searchResults) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!searchResults) {
                return '';
            }
            const changed = yield Promise.all(files
                .map((file, i) => __awaiter(this, void 0, void 0, function* () {
                const result = yield Promise.all(repeatableContent.map((s) => __awaiter(this, void 0, void 0, function* () { return yield this.applyTemplateToSearchResults(searchResults, file, s, i); })));
                return result.join('\n');
            })));
            return changed.join('\n');
        });
    }
    parseTemplate(prefixes, templateContent) {
        const isHeader = (line) => line.startsWith(prefixes.header);
        const isFooter = (line) => line.startsWith(prefixes.footer);
        const isRepeat = (line) => !isHeader(line) && !isFooter(line);
        const heading = templateContent.filter(isHeader).map((s) => s.slice(1)).join('\n');
        const footer = templateContent.filter(isFooter).map((s) => s.slice(1)).join('\n');
        const repeatableContent = templateContent.filter(isRepeat).filter(e => e).length === 0
            ? [this.config.defaultTemplate]
            : templateContent.filter(isRepeat).filter(e => e);
        return { heading, footer, repeatableContent };
    }
    saveLeftPanelState() {
        this.leftPanelInfo = {
            collapsed: this.app.workspace.leftSplit.collapsed,
            tab: this.getSearchTabIndex(),
            text: this.getSearchValue(),
        };
    }
    restoreLeftPanelState() {
        const { collapsed, tab, text } = this.leftPanelInfo;
        const splitChildren = this.getLeftSplitElement();
        this.getSearchView().searchComponent.setValue(text);
        if (tab !== splitChildren.currentTab) {
            splitChildren.selectTabIndex(tab);
        }
        if (collapsed) {
            this.app.workspace.leftSplit.collapse();
        }
    }
    search(s) {
        // @ts-ignore
        const globalSearchFn = this.app.internalPlugins.getPluginById('global-search').instance.openGlobalSearch.bind(this);
        const search = (query) => globalSearchFn(query);
        search(s);
    }
    getLeftSplitElement() {
        // @ts-ignore
        return this.app.workspace.leftSplit.children[0];
    }
    getSearchView() {
        const view = this.getLeftSplitElement().children.filter(e => e.getViewState().type === 'search')[0].view;
        if ('searchComponent' in view) {
            return view;
        }
        return undefined;
    }
    getSearchValue() {
        const view = this.getSearchView();
        if (view) {
            return view.searchComponent.getValue();
        }
        return '';
    }
    getSearchTabIndex() {
        const leftTabs = this.getLeftSplitElement().children;
        let searchTabId;
        this.app.workspace.iterateAllLeaves((leaf) => {
            if (leaf.getViewState().type == "search") {
                searchTabId = leaf.id;
            }
        });
        return leftTabs.findIndex((item, _index, _array) => {
            if (item.id == searchTabId) {
                return true;
            }
        });
    }
    ;
    getFoundAfterDelay(immediate) {
        return __awaiter(this, void 0, void 0, function* () {
            const searchLeaf = this.app.workspace.getLeavesOfType('search')[0];
            const view = yield searchLeaf.open(searchLeaf.view);
            if (immediate) {
                // @ts-ignore
                return Promise.resolve(view.dom.resultDomLookup);
            }
            return new Promise(resolve => {
                setTimeout(() => {
                    // @ts-ignore
                    return resolve(view.dom.resultDomLookup);
                }, this.config.delay);
            });
        });
    }
    applyTemplateToSearchResults(searchResults, file, template, index) {
        return __awaiter(this, void 0, void 0, function* () {
            const fileContent = (new RegExp(this.seqs.filter(e => e.readContent).map(e => e.name).join('|')).test(template))
                ? yield this.app.vault.cachedRead(file)
                : '';
            return this.seqs.reduce((acc, seq) => acc.replace(new RegExp(seq.name, 'gu'), replace => seq.format(this, replace, fileContent, file, searchResults.get(file), index)), template);
        });
    }
    clearOldResultsInFile(content, query, lineEnding) {
        var _a;
        const lastLine = getLastLineToReplace(content, query, this.config.lineEnding);
        this.cm.replaceRange('\n' + lineEnding, { line: query.end + 1, ch: 0 }, { line: lastLine, ch: ((_a = this.cm.getLine(lastLine)) === null || _a === void 0 ? void 0 : _a.length) || 0 });
    }
}
class SettingTab extends obsidian.PluginSettingTab {
    constructor(app, plugin) {
        super(app, plugin);
        this.app = app;
        this.plugin = plugin;
    }
    display() {
        let { containerEl } = this;
        containerEl.empty();
        containerEl.createEl('h2', { text: 'Settings for Text Expander' });
        new obsidian.Setting(containerEl)
            .setName('Auto Expand')
            .setDesc('Expand all queries in a file once you open it')
            .addToggle(toggle => {
            toggle
                .setValue(this.plugin.config.autoExpand)
                .onChange(value => {
                this.plugin.config.autoExpand = value;
                this.plugin.saveSettings();
            });
        });
        new obsidian.Setting(containerEl)
            .setName('Delay')
            .setDesc('Text expander don\' wait until search completed. It waits for a delay and paste result after that.')
            .addSlider(slider => {
            slider.setLimits(100, 10000, 100);
            slider.setValue(this.plugin.config.delay);
            slider.onChange(value => {
                this.plugin.config.delay = value;
                this.plugin.saveSettings();
            });
            slider.setDynamicTooltip();
        });
        new obsidian.Setting(containerEl)
            .setName('Line ending')
            .setDesc('You can specify the text which will appear at the bottom of the generated text.')
            .addText(text => {
            text.setValue(this.plugin.config.lineEnding)
                .onChange(val => {
                this.plugin.config.lineEnding = val;
                this.plugin.saveSettings();
            });
        });
        new obsidian.Setting(containerEl)
            .setName('Default template')
            .setDesc('You can specify default template')
            .addTextArea(text => {
            text.setValue(this.plugin.config.defaultTemplate)
                .onChange(val => {
                this.plugin.config.defaultTemplate = val;
                this.plugin.saveSettings();
            });
        });
        new obsidian.Setting(containerEl)
            .setName('Exclude current file')
            .setDesc('You can specify should text expander exclude results from current file or not')
            .addToggle(toggle => {
            toggle
                .setValue(this.plugin.config.excludeCurrent)
                .onChange(value => {
                this.plugin.config.excludeCurrent = value;
                this.plugin.saveSettings();
            });
        });
        new obsidian.Setting(containerEl)
            .setHeading()
            .setName('Prefixes');
        new obsidian.Setting(containerEl)
            .setName('Header')
            .setDesc('Line prefixed by this symbol will be recognized as header')
            .addText(text => {
            text.setValue(this.plugin.config.prefixes.header)
                .onChange(val => {
                this.plugin.config.prefixes.header = val;
                this.plugin.saveSettings();
            });
        });
        new obsidian.Setting(containerEl)
            .setName('Footer')
            .setDesc('Line prefixed by this symbol will be recognized as footer')
            .addText(text => {
            text.setValue(this.plugin.config.prefixes.footer)
                .onChange(val => {
                this.plugin.config.prefixes.footer = val;
                this.plugin.saveSettings();
            });
        });
        new obsidian.Setting(containerEl)
            .setName('Sequences')
            .setDesc('REGEXP - DESCRIPTION')
            .setDesc((() => {
            const fragment = new DocumentFragment();
            const div = fragment.createEl('div');
            this.plugin.seqs
                .map(e => e.name + ' - ' + (e.desc || ''))
                .map(e => {
                const el = fragment.createEl('div');
                el.setText(e);
                el.setAttribute('style', `
                                border-bottom: 1px solid rgba(255, 255, 255, 0.1);
                                margin-bottom: 0.5rem;
                                padding-bottom: 0.5rem;
                            `);
                return el;
            }).forEach(el => {
                div.appendChild(el);
            });
            fragment.appendChild(div);
            return fragment;
        })());
    }
}

module.exports = TextExpander;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,[encoded data, 130012 characters]
