13647 lines
445 KiB
JavaScript
13647 lines
445 KiB
JavaScript
/*
|
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
|
if you want to view the source, please visit the github repository of this plugin
|
|
*/
|
|
|
|
var __create = Object.create;
|
|
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __getProtoOf = Object.getPrototypeOf;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __export = (target, all) => {
|
|
for (var name in all)
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
};
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
for (let key of __getOwnPropNames(from))
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
}
|
|
return to;
|
|
};
|
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
// If the importer is in node compatibility mode or this is not an ESM
|
|
// file that has been converted to a CommonJS file using a Babel-
|
|
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
mod
|
|
));
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
|
|
// src/main.ts
|
|
var main_exports = {};
|
|
__export(main_exports, {
|
|
DEFAULT_SETTINGS: () => DEFAULT_SETTINGS,
|
|
default: () => CssEditorPlugin
|
|
});
|
|
module.exports = __toCommonJS(main_exports);
|
|
var import_obsidian11 = require("obsidian");
|
|
|
|
// src/views/CssEditorView.ts
|
|
var import_obsidian7 = require("obsidian");
|
|
var import_view8 = require("@codemirror/view");
|
|
|
|
// node_modules/@replit/codemirror-vim/dist/index.js
|
|
var import_state = require("@codemirror/state");
|
|
var import_language = require("@codemirror/language");
|
|
var View = __toESM(require("@codemirror/view"), 1);
|
|
var import_view = require("@codemirror/view");
|
|
var import_search = require("@codemirror/search");
|
|
var import_commands = require("@codemirror/commands");
|
|
function initVim(CM) {
|
|
var Pos2 = CM.Pos;
|
|
function updateSelectionForSurrogateCharacters(cm, curStart, curEnd) {
|
|
if (curStart.line === curEnd.line && curStart.ch >= curEnd.ch - 1) {
|
|
var text = cm.getLine(curStart.line);
|
|
var charCode = text.charCodeAt(curStart.ch);
|
|
if (55296 <= charCode && charCode <= 55551) {
|
|
curEnd.ch += 1;
|
|
}
|
|
}
|
|
return { start: curStart, end: curEnd };
|
|
}
|
|
var defaultKeymap2 = [
|
|
// Key to key mapping. This goes first to make it possible to override
|
|
// existing mappings.
|
|
{ keys: "<Left>", type: "keyToKey", toKeys: "h" },
|
|
{ keys: "<Right>", type: "keyToKey", toKeys: "l" },
|
|
{ keys: "<Up>", type: "keyToKey", toKeys: "k" },
|
|
{ keys: "<Down>", type: "keyToKey", toKeys: "j" },
|
|
{ keys: "g<Up>", type: "keyToKey", toKeys: "gk" },
|
|
{ keys: "g<Down>", type: "keyToKey", toKeys: "gj" },
|
|
{ keys: "<Space>", type: "keyToKey", toKeys: "l" },
|
|
{ keys: "<BS>", type: "keyToKey", toKeys: "h" },
|
|
{ keys: "<Del>", type: "keyToKey", toKeys: "x" },
|
|
{ keys: "<C-Space>", type: "keyToKey", toKeys: "W" },
|
|
{ keys: "<C-BS>", type: "keyToKey", toKeys: "B" },
|
|
{ keys: "<S-Space>", type: "keyToKey", toKeys: "w" },
|
|
{ keys: "<S-BS>", type: "keyToKey", toKeys: "b" },
|
|
{ keys: "<C-n>", type: "keyToKey", toKeys: "j" },
|
|
{ keys: "<C-p>", type: "keyToKey", toKeys: "k" },
|
|
{ keys: "<C-[>", type: "keyToKey", toKeys: "<Esc>" },
|
|
{ keys: "<C-c>", type: "keyToKey", toKeys: "<Esc>" },
|
|
{ keys: "<C-[>", type: "keyToKey", toKeys: "<Esc>", context: "insert" },
|
|
{ keys: "<C-c>", type: "keyToKey", toKeys: "<Esc>", context: "insert" },
|
|
{ keys: "<C-Esc>", type: "keyToKey", toKeys: "<Esc>" },
|
|
// ipad keyboard sends C-Esc instead of C-[
|
|
{ keys: "<C-Esc>", type: "keyToKey", toKeys: "<Esc>", context: "insert" },
|
|
{ keys: "s", type: "keyToKey", toKeys: "cl", context: "normal" },
|
|
{ keys: "s", type: "keyToKey", toKeys: "c", context: "visual" },
|
|
{ keys: "S", type: "keyToKey", toKeys: "cc", context: "normal" },
|
|
{ keys: "S", type: "keyToKey", toKeys: "VdO", context: "visual" },
|
|
{ keys: "<Home>", type: "keyToKey", toKeys: "0" },
|
|
{ keys: "<End>", type: "keyToKey", toKeys: "$" },
|
|
{ keys: "<PageUp>", type: "keyToKey", toKeys: "<C-b>" },
|
|
{ keys: "<PageDown>", type: "keyToKey", toKeys: "<C-f>" },
|
|
{ keys: "<CR>", type: "keyToKey", toKeys: "j^", context: "normal" },
|
|
{ keys: "<Ins>", type: "keyToKey", toKeys: "i", context: "normal" },
|
|
{ keys: "<Ins>", type: "action", action: "toggleOverwrite", context: "insert" },
|
|
// Motions
|
|
{ keys: "H", type: "motion", motion: "moveToTopLine", motionArgs: { linewise: true, toJumplist: true } },
|
|
{ keys: "M", type: "motion", motion: "moveToMiddleLine", motionArgs: { linewise: true, toJumplist: true } },
|
|
{ keys: "L", type: "motion", motion: "moveToBottomLine", motionArgs: { linewise: true, toJumplist: true } },
|
|
{ keys: "h", type: "motion", motion: "moveByCharacters", motionArgs: { forward: false } },
|
|
{ keys: "l", type: "motion", motion: "moveByCharacters", motionArgs: { forward: true } },
|
|
{ keys: "j", type: "motion", motion: "moveByLines", motionArgs: { forward: true, linewise: true } },
|
|
{ keys: "k", type: "motion", motion: "moveByLines", motionArgs: { forward: false, linewise: true } },
|
|
{ keys: "gj", type: "motion", motion: "moveByDisplayLines", motionArgs: { forward: true } },
|
|
{ keys: "gk", type: "motion", motion: "moveByDisplayLines", motionArgs: { forward: false } },
|
|
{ keys: "w", type: "motion", motion: "moveByWords", motionArgs: { forward: true, wordEnd: false } },
|
|
{ keys: "W", type: "motion", motion: "moveByWords", motionArgs: { forward: true, wordEnd: false, bigWord: true } },
|
|
{ keys: "e", type: "motion", motion: "moveByWords", motionArgs: { forward: true, wordEnd: true, inclusive: true } },
|
|
{ keys: "E", type: "motion", motion: "moveByWords", motionArgs: { forward: true, wordEnd: true, bigWord: true, inclusive: true } },
|
|
{ keys: "b", type: "motion", motion: "moveByWords", motionArgs: { forward: false, wordEnd: false } },
|
|
{ keys: "B", type: "motion", motion: "moveByWords", motionArgs: { forward: false, wordEnd: false, bigWord: true } },
|
|
{ keys: "ge", type: "motion", motion: "moveByWords", motionArgs: { forward: false, wordEnd: true, inclusive: true } },
|
|
{ keys: "gE", type: "motion", motion: "moveByWords", motionArgs: { forward: false, wordEnd: true, bigWord: true, inclusive: true } },
|
|
{ keys: "{", type: "motion", motion: "moveByParagraph", motionArgs: { forward: false, toJumplist: true } },
|
|
{ keys: "}", type: "motion", motion: "moveByParagraph", motionArgs: { forward: true, toJumplist: true } },
|
|
{ keys: "(", type: "motion", motion: "moveBySentence", motionArgs: { forward: false } },
|
|
{ keys: ")", type: "motion", motion: "moveBySentence", motionArgs: { forward: true } },
|
|
{ keys: "<C-f>", type: "motion", motion: "moveByPage", motionArgs: { forward: true } },
|
|
{ keys: "<C-b>", type: "motion", motion: "moveByPage", motionArgs: { forward: false } },
|
|
{ keys: "<C-d>", type: "motion", motion: "moveByScroll", motionArgs: { forward: true, explicitRepeat: true } },
|
|
{ keys: "<C-u>", type: "motion", motion: "moveByScroll", motionArgs: { forward: false, explicitRepeat: true } },
|
|
{ keys: "gg", type: "motion", motion: "moveToLineOrEdgeOfDocument", motionArgs: { forward: false, explicitRepeat: true, linewise: true, toJumplist: true } },
|
|
{ keys: "G", type: "motion", motion: "moveToLineOrEdgeOfDocument", motionArgs: { forward: true, explicitRepeat: true, linewise: true, toJumplist: true } },
|
|
{ keys: "g$", type: "motion", motion: "moveToEndOfDisplayLine" },
|
|
{ keys: "g^", type: "motion", motion: "moveToStartOfDisplayLine" },
|
|
{ keys: "g0", type: "motion", motion: "moveToStartOfDisplayLine" },
|
|
{ keys: "0", type: "motion", motion: "moveToStartOfLine" },
|
|
{ keys: "^", type: "motion", motion: "moveToFirstNonWhiteSpaceCharacter" },
|
|
{ keys: "+", type: "motion", motion: "moveByLines", motionArgs: { forward: true, toFirstChar: true } },
|
|
{ keys: "-", type: "motion", motion: "moveByLines", motionArgs: { forward: false, toFirstChar: true } },
|
|
{ keys: "_", type: "motion", motion: "moveByLines", motionArgs: { forward: true, toFirstChar: true, repeatOffset: -1 } },
|
|
{ keys: "$", type: "motion", motion: "moveToEol", motionArgs: { inclusive: true } },
|
|
{ keys: "%", type: "motion", motion: "moveToMatchedSymbol", motionArgs: { inclusive: true, toJumplist: true } },
|
|
{ keys: "f<character>", type: "motion", motion: "moveToCharacter", motionArgs: { forward: true, inclusive: true } },
|
|
{ keys: "F<character>", type: "motion", motion: "moveToCharacter", motionArgs: { forward: false } },
|
|
{ keys: "t<character>", type: "motion", motion: "moveTillCharacter", motionArgs: { forward: true, inclusive: true } },
|
|
{ keys: "T<character>", type: "motion", motion: "moveTillCharacter", motionArgs: { forward: false } },
|
|
{ keys: ";", type: "motion", motion: "repeatLastCharacterSearch", motionArgs: { forward: true } },
|
|
{ keys: ",", type: "motion", motion: "repeatLastCharacterSearch", motionArgs: { forward: false } },
|
|
{ keys: "'<register>", type: "motion", motion: "goToMark", motionArgs: { toJumplist: true, linewise: true } },
|
|
{ keys: "`<register>", type: "motion", motion: "goToMark", motionArgs: { toJumplist: true } },
|
|
{ keys: "]`", type: "motion", motion: "jumpToMark", motionArgs: { forward: true } },
|
|
{ keys: "[`", type: "motion", motion: "jumpToMark", motionArgs: { forward: false } },
|
|
{ keys: "]'", type: "motion", motion: "jumpToMark", motionArgs: { forward: true, linewise: true } },
|
|
{ keys: "['", type: "motion", motion: "jumpToMark", motionArgs: { forward: false, linewise: true } },
|
|
// the next two aren't motions but must come before more general motion declarations
|
|
{ keys: "]p", type: "action", action: "paste", isEdit: true, actionArgs: { after: true, isEdit: true, matchIndent: true } },
|
|
{ keys: "[p", type: "action", action: "paste", isEdit: true, actionArgs: { after: false, isEdit: true, matchIndent: true } },
|
|
{ keys: "]<character>", type: "motion", motion: "moveToSymbol", motionArgs: { forward: true, toJumplist: true } },
|
|
{ keys: "[<character>", type: "motion", motion: "moveToSymbol", motionArgs: { forward: false, toJumplist: true } },
|
|
{ keys: "|", type: "motion", motion: "moveToColumn" },
|
|
{ keys: "o", type: "motion", motion: "moveToOtherHighlightedEnd", context: "visual" },
|
|
{ keys: "O", type: "motion", motion: "moveToOtherHighlightedEnd", motionArgs: { sameLine: true }, context: "visual" },
|
|
// Operators
|
|
{ keys: "d", type: "operator", operator: "delete" },
|
|
{ keys: "y", type: "operator", operator: "yank" },
|
|
{ keys: "c", type: "operator", operator: "change" },
|
|
{ keys: "=", type: "operator", operator: "indentAuto" },
|
|
{ keys: ">", type: "operator", operator: "indent", operatorArgs: { indentRight: true } },
|
|
{ keys: "<", type: "operator", operator: "indent", operatorArgs: { indentRight: false } },
|
|
{ keys: "g~", type: "operator", operator: "changeCase" },
|
|
{ keys: "gu", type: "operator", operator: "changeCase", operatorArgs: { toLower: true }, isEdit: true },
|
|
{ keys: "gU", type: "operator", operator: "changeCase", operatorArgs: { toLower: false }, isEdit: true },
|
|
{ keys: "n", type: "motion", motion: "findNext", motionArgs: { forward: true, toJumplist: true } },
|
|
{ keys: "N", type: "motion", motion: "findNext", motionArgs: { forward: false, toJumplist: true } },
|
|
{ keys: "gn", type: "motion", motion: "findAndSelectNextInclusive", motionArgs: { forward: true } },
|
|
{ keys: "gN", type: "motion", motion: "findAndSelectNextInclusive", motionArgs: { forward: false } },
|
|
{ keys: "gq", type: "operator", operator: "hardWrap" },
|
|
{ keys: "gw", type: "operator", operator: "hardWrap", operatorArgs: { keepCursor: true } },
|
|
{ keys: "g?", type: "operator", operator: "rot13" },
|
|
// Operator-Motion dual commands
|
|
{ keys: "x", type: "operatorMotion", operator: "delete", motion: "moveByCharacters", motionArgs: { forward: true }, operatorMotionArgs: { visualLine: false } },
|
|
{ keys: "X", type: "operatorMotion", operator: "delete", motion: "moveByCharacters", motionArgs: { forward: false }, operatorMotionArgs: { visualLine: true } },
|
|
{ keys: "D", type: "operatorMotion", operator: "delete", motion: "moveToEol", motionArgs: { inclusive: true }, context: "normal" },
|
|
{ keys: "D", type: "operator", operator: "delete", operatorArgs: { linewise: true }, context: "visual" },
|
|
{ keys: "Y", type: "operatorMotion", operator: "yank", motion: "expandToLine", motionArgs: { linewise: true }, context: "normal" },
|
|
{ keys: "Y", type: "operator", operator: "yank", operatorArgs: { linewise: true }, context: "visual" },
|
|
{ keys: "C", type: "operatorMotion", operator: "change", motion: "moveToEol", motionArgs: { inclusive: true }, context: "normal" },
|
|
{ keys: "C", type: "operator", operator: "change", operatorArgs: { linewise: true }, context: "visual" },
|
|
{ keys: "~", type: "operatorMotion", operator: "changeCase", motion: "moveByCharacters", motionArgs: { forward: true }, operatorArgs: { shouldMoveCursor: true }, context: "normal" },
|
|
{ keys: "~", type: "operator", operator: "changeCase", context: "visual" },
|
|
{ keys: "<C-u>", type: "operatorMotion", operator: "delete", motion: "moveToStartOfLine", context: "insert" },
|
|
{ keys: "<C-w>", type: "operatorMotion", operator: "delete", motion: "moveByWords", motionArgs: { forward: false, wordEnd: false }, context: "insert" },
|
|
//ignore C-w in normal mode
|
|
{ keys: "<C-w>", type: "idle", context: "normal" },
|
|
// Actions
|
|
{ keys: "<C-i>", type: "action", action: "jumpListWalk", actionArgs: { forward: true } },
|
|
{ keys: "<C-o>", type: "action", action: "jumpListWalk", actionArgs: { forward: false } },
|
|
{ keys: "<C-e>", type: "action", action: "scroll", actionArgs: { forward: true, linewise: true } },
|
|
{ keys: "<C-y>", type: "action", action: "scroll", actionArgs: { forward: false, linewise: true } },
|
|
{ keys: "a", type: "action", action: "enterInsertMode", isEdit: true, actionArgs: { insertAt: "charAfter" }, context: "normal" },
|
|
{ keys: "A", type: "action", action: "enterInsertMode", isEdit: true, actionArgs: { insertAt: "eol" }, context: "normal" },
|
|
{ keys: "A", type: "action", action: "enterInsertMode", isEdit: true, actionArgs: { insertAt: "endOfSelectedArea" }, context: "visual" },
|
|
{ keys: "i", type: "action", action: "enterInsertMode", isEdit: true, actionArgs: { insertAt: "inplace" }, context: "normal" },
|
|
{ keys: "gi", type: "action", action: "enterInsertMode", isEdit: true, actionArgs: { insertAt: "lastEdit" }, context: "normal" },
|
|
{ keys: "I", type: "action", action: "enterInsertMode", isEdit: true, actionArgs: { insertAt: "firstNonBlank" }, context: "normal" },
|
|
{ keys: "gI", type: "action", action: "enterInsertMode", isEdit: true, actionArgs: { insertAt: "bol" }, context: "normal" },
|
|
{ keys: "I", type: "action", action: "enterInsertMode", isEdit: true, actionArgs: { insertAt: "startOfSelectedArea" }, context: "visual" },
|
|
{ keys: "o", type: "action", action: "newLineAndEnterInsertMode", isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: true }, context: "normal" },
|
|
{ keys: "O", type: "action", action: "newLineAndEnterInsertMode", isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: false }, context: "normal" },
|
|
{ keys: "v", type: "action", action: "toggleVisualMode" },
|
|
{ keys: "V", type: "action", action: "toggleVisualMode", actionArgs: { linewise: true } },
|
|
{ keys: "<C-v>", type: "action", action: "toggleVisualMode", actionArgs: { blockwise: true } },
|
|
{ keys: "<C-q>", type: "action", action: "toggleVisualMode", actionArgs: { blockwise: true } },
|
|
{ keys: "gv", type: "action", action: "reselectLastSelection" },
|
|
{ keys: "J", type: "action", action: "joinLines", isEdit: true },
|
|
{ keys: "gJ", type: "action", action: "joinLines", actionArgs: { keepSpaces: true }, isEdit: true },
|
|
{ keys: "p", type: "action", action: "paste", isEdit: true, actionArgs: { after: true, isEdit: true } },
|
|
{ keys: "P", type: "action", action: "paste", isEdit: true, actionArgs: { after: false, isEdit: true } },
|
|
{ keys: "r<character>", type: "action", action: "replace", isEdit: true },
|
|
{ keys: "@<register>", type: "action", action: "replayMacro" },
|
|
{ keys: "q<register>", type: "action", action: "enterMacroRecordMode" },
|
|
// Handle Replace-mode as a special case of insert mode.
|
|
{ keys: "R", type: "action", action: "enterInsertMode", isEdit: true, actionArgs: { replace: true }, context: "normal" },
|
|
{ keys: "R", type: "operator", operator: "change", operatorArgs: { linewise: true, fullLine: true }, context: "visual", exitVisualBlock: true },
|
|
{ keys: "u", type: "action", action: "undo", context: "normal" },
|
|
{ keys: "u", type: "operator", operator: "changeCase", operatorArgs: { toLower: true }, context: "visual", isEdit: true },
|
|
{ keys: "U", type: "operator", operator: "changeCase", operatorArgs: { toLower: false }, context: "visual", isEdit: true },
|
|
{ keys: "<C-r>", type: "action", action: "redo" },
|
|
{ keys: "m<register>", type: "action", action: "setMark" },
|
|
{ keys: '"<register>', type: "action", action: "setRegister" },
|
|
{ keys: "<C-r><register>", type: "action", action: "insertRegister", context: "insert", isEdit: true },
|
|
{ keys: "<C-o>", type: "action", action: "oneNormalCommand", context: "insert" },
|
|
{ keys: "zz", type: "action", action: "scrollToCursor", actionArgs: { position: "center" } },
|
|
{ keys: "z.", type: "action", action: "scrollToCursor", actionArgs: { position: "center" }, motion: "moveToFirstNonWhiteSpaceCharacter" },
|
|
{ keys: "zt", type: "action", action: "scrollToCursor", actionArgs: { position: "top" } },
|
|
{ keys: "z<CR>", type: "action", action: "scrollToCursor", actionArgs: { position: "top" }, motion: "moveToFirstNonWhiteSpaceCharacter" },
|
|
{ keys: "zb", type: "action", action: "scrollToCursor", actionArgs: { position: "bottom" } },
|
|
{ keys: "z-", type: "action", action: "scrollToCursor", actionArgs: { position: "bottom" }, motion: "moveToFirstNonWhiteSpaceCharacter" },
|
|
{ keys: ".", type: "action", action: "repeatLastEdit" },
|
|
{ keys: "<C-a>", type: "action", action: "incrementNumberToken", isEdit: true, actionArgs: { increase: true, backtrack: false } },
|
|
{ keys: "<C-x>", type: "action", action: "incrementNumberToken", isEdit: true, actionArgs: { increase: false, backtrack: false } },
|
|
{ keys: "<C-t>", type: "action", action: "indent", actionArgs: { indentRight: true }, context: "insert" },
|
|
{ keys: "<C-d>", type: "action", action: "indent", actionArgs: { indentRight: false }, context: "insert" },
|
|
// Text object motions
|
|
{ keys: "a<register>", type: "motion", motion: "textObjectManipulation" },
|
|
{ keys: "i<register>", type: "motion", motion: "textObjectManipulation", motionArgs: { textObjectInner: true } },
|
|
// Search
|
|
{ keys: "/", type: "search", searchArgs: { forward: true, querySrc: "prompt", toJumplist: true } },
|
|
{ keys: "?", type: "search", searchArgs: { forward: false, querySrc: "prompt", toJumplist: true } },
|
|
{ keys: "*", type: "search", searchArgs: { forward: true, querySrc: "wordUnderCursor", wholeWordOnly: true, toJumplist: true } },
|
|
{ keys: "#", type: "search", searchArgs: { forward: false, querySrc: "wordUnderCursor", wholeWordOnly: true, toJumplist: true } },
|
|
{ keys: "g*", type: "search", searchArgs: { forward: true, querySrc: "wordUnderCursor", toJumplist: true } },
|
|
{ keys: "g#", type: "search", searchArgs: { forward: false, querySrc: "wordUnderCursor", toJumplist: true } },
|
|
// Ex command
|
|
{ keys: ":", type: "ex" }
|
|
];
|
|
var usedKeys = /* @__PURE__ */ Object.create(null);
|
|
var defaultKeymapLength = defaultKeymap2.length;
|
|
var defaultExCommandMap = [
|
|
{ name: "colorscheme", shortName: "colo" },
|
|
{ name: "map" },
|
|
{ name: "imap", shortName: "im" },
|
|
{ name: "nmap", shortName: "nm" },
|
|
{ name: "vmap", shortName: "vm" },
|
|
{ name: "omap", shortName: "om" },
|
|
{ name: "noremap", shortName: "no" },
|
|
{ name: "nnoremap", shortName: "nn" },
|
|
{ name: "vnoremap", shortName: "vn" },
|
|
{ name: "inoremap", shortName: "ino" },
|
|
{ name: "onoremap", shortName: "ono" },
|
|
{ name: "unmap" },
|
|
{ name: "mapclear", shortName: "mapc" },
|
|
{ name: "nmapclear", shortName: "nmapc" },
|
|
{ name: "vmapclear", shortName: "vmapc" },
|
|
{ name: "imapclear", shortName: "imapc" },
|
|
{ name: "omapclear", shortName: "omapc" },
|
|
{ name: "write", shortName: "w" },
|
|
{ name: "undo", shortName: "u" },
|
|
{ name: "redo", shortName: "red" },
|
|
{ name: "set", shortName: "se" },
|
|
{ name: "setlocal", shortName: "setl" },
|
|
{ name: "setglobal", shortName: "setg" },
|
|
{ name: "sort", shortName: "sor" },
|
|
{ name: "substitute", shortName: "s", possiblyAsync: true },
|
|
{ name: "startinsert", shortName: "start" },
|
|
{ name: "nohlsearch", shortName: "noh" },
|
|
{ name: "yank", shortName: "y" },
|
|
{ name: "delmarks", shortName: "delm" },
|
|
{ name: "marks", excludeFromCommandHistory: true },
|
|
{ name: "registers", shortName: "reg", excludeFromCommandHistory: true },
|
|
{ name: "vglobal", shortName: "v" },
|
|
{ name: "delete", shortName: "d" },
|
|
{ name: "join", shortName: "j" },
|
|
{ name: "normal", shortName: "norm" },
|
|
{ name: "global", shortName: "g" }
|
|
];
|
|
var langmap = parseLangmap("");
|
|
function enterVimMode(cm) {
|
|
cm.setOption("disableInput", true);
|
|
cm.setOption("showCursorWhenSelecting", false);
|
|
CM.signal(cm, "vim-mode-change", { mode: "normal" });
|
|
cm.on("cursorActivity", onCursorActivity);
|
|
maybeInitVimState(cm);
|
|
CM.on(cm.getInputField(), "paste", getOnPasteFn(cm));
|
|
}
|
|
function leaveVimMode(cm) {
|
|
cm.setOption("disableInput", false);
|
|
cm.off("cursorActivity", onCursorActivity);
|
|
CM.off(cm.getInputField(), "paste", getOnPasteFn(cm));
|
|
cm.state.vim = null;
|
|
if (highlightTimeout) clearTimeout(highlightTimeout);
|
|
}
|
|
function getOnPasteFn(cm) {
|
|
var vim2 = cm.state.vim;
|
|
if (!vim2.onPasteFn) {
|
|
vim2.onPasteFn = function() {
|
|
if (!vim2.insertMode) {
|
|
cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));
|
|
actions.enterInsertMode(cm, {}, vim2);
|
|
}
|
|
};
|
|
}
|
|
return vim2.onPasteFn;
|
|
}
|
|
var numberRegex = /[\d]/;
|
|
var wordCharTest = [CM.isWordChar, function(ch) {
|
|
return ch && !CM.isWordChar(ch) && !/\s/.test(ch);
|
|
}], bigWordCharTest = [function(ch) {
|
|
return /\S/.test(ch);
|
|
}];
|
|
var validMarks = ["<", ">"];
|
|
var validRegisters = ["-", '"', ".", ":", "_", "/", "+"];
|
|
var latinCharRegex = /^\w$/;
|
|
var upperCaseChars = /^[A-Z]$/;
|
|
try {
|
|
upperCaseChars = new RegExp("^[\\p{Lu}]$", "u");
|
|
} catch (_) {
|
|
}
|
|
function isLine(cm, line) {
|
|
return line >= cm.firstLine() && line <= cm.lastLine();
|
|
}
|
|
function isLowerCase(k) {
|
|
return /^[a-z]$/.test(k);
|
|
}
|
|
function isMatchableSymbol(k) {
|
|
return "()[]{}".indexOf(k) != -1;
|
|
}
|
|
function isNumber(k) {
|
|
return numberRegex.test(k);
|
|
}
|
|
function isUpperCase(k) {
|
|
return upperCaseChars.test(k);
|
|
}
|
|
function isWhiteSpaceString(k) {
|
|
return /^\s*$/.test(k);
|
|
}
|
|
function isEndOfSentenceSymbol(k) {
|
|
return ".?!".indexOf(k) != -1;
|
|
}
|
|
function inArray(val, arr) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
if (arr[i] == val) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
var options = {};
|
|
function defineOption(name, defaultValue, type, aliases, callback) {
|
|
if (defaultValue === void 0 && !callback) {
|
|
throw Error("defaultValue is required unless callback is provided");
|
|
}
|
|
if (!type) {
|
|
type = "string";
|
|
}
|
|
options[name] = {
|
|
type,
|
|
defaultValue,
|
|
callback
|
|
};
|
|
if (aliases) {
|
|
for (var i = 0; i < aliases.length; i++) {
|
|
options[aliases[i]] = options[name];
|
|
}
|
|
}
|
|
if (defaultValue) {
|
|
setOption(name, defaultValue);
|
|
}
|
|
}
|
|
function setOption(name, value, cm, cfg) {
|
|
var option = options[name];
|
|
cfg = cfg || {};
|
|
var scope = cfg.scope;
|
|
if (!option) {
|
|
return new Error("Unknown option: " + name);
|
|
}
|
|
if (option.type == "boolean") {
|
|
if (value && value !== true) {
|
|
return new Error("Invalid argument: " + name + "=" + value);
|
|
} else if (value !== false) {
|
|
value = true;
|
|
}
|
|
}
|
|
if (option.callback) {
|
|
if (scope !== "local") {
|
|
option.callback(value, void 0);
|
|
}
|
|
if (scope !== "global" && cm) {
|
|
option.callback(value, cm);
|
|
}
|
|
} else {
|
|
if (scope !== "local") {
|
|
option.value = option.type == "boolean" ? !!value : value;
|
|
}
|
|
if (scope !== "global" && cm) {
|
|
cm.state.vim.options[name] = { value };
|
|
}
|
|
}
|
|
}
|
|
function getOption(name, cm, cfg) {
|
|
var option = options[name];
|
|
cfg = cfg || {};
|
|
var scope = cfg.scope;
|
|
if (!option) {
|
|
return new Error("Unknown option: " + name);
|
|
}
|
|
if (option.callback) {
|
|
let local = cm && option.callback(void 0, cm);
|
|
if (scope !== "global" && local !== void 0) {
|
|
return local;
|
|
}
|
|
if (scope !== "local") {
|
|
return option.callback();
|
|
}
|
|
return;
|
|
} else {
|
|
let local = scope !== "global" && (cm && cm.state.vim.options[name]);
|
|
return (local || scope !== "local" && option || {}).value;
|
|
}
|
|
}
|
|
defineOption("filetype", void 0, "string", ["ft"], function(name, cm) {
|
|
if (cm === void 0) {
|
|
return;
|
|
}
|
|
if (name === void 0) {
|
|
let mode = cm.getOption("mode");
|
|
return mode == "null" ? "" : mode;
|
|
} else {
|
|
let mode = name == "" ? "null" : name;
|
|
cm.setOption("mode", mode);
|
|
}
|
|
});
|
|
defineOption("textwidth", 80, "number", ["tw"], function(width, cm) {
|
|
if (cm === void 0) {
|
|
return;
|
|
}
|
|
if (width === void 0) {
|
|
var value = cm.getOption("textwidth");
|
|
return value;
|
|
} else {
|
|
var column = Math.round(width);
|
|
if (column > 1) {
|
|
cm.setOption("textwidth", column);
|
|
}
|
|
}
|
|
});
|
|
var createCircularJumpList = function() {
|
|
var size = 100;
|
|
var pointer = -1;
|
|
var head = 0;
|
|
var tail = 0;
|
|
var buffer = (
|
|
/**@type {(Marker|undefined)[]} */
|
|
new Array(size)
|
|
);
|
|
function add(cm, oldCur, newCur) {
|
|
var current = pointer % size;
|
|
var curMark = buffer[current];
|
|
function useNextSlot(cursor) {
|
|
var next = ++pointer % size;
|
|
var trashMark = buffer[next];
|
|
if (trashMark) {
|
|
trashMark.clear();
|
|
}
|
|
buffer[next] = cm.setBookmark(cursor);
|
|
}
|
|
if (curMark) {
|
|
var markPos = curMark.find();
|
|
if (markPos && !cursorEqual(markPos, oldCur)) {
|
|
useNextSlot(oldCur);
|
|
}
|
|
} else {
|
|
useNextSlot(oldCur);
|
|
}
|
|
useNextSlot(newCur);
|
|
head = pointer;
|
|
tail = pointer - size + 1;
|
|
if (tail < 0) {
|
|
tail = 0;
|
|
}
|
|
}
|
|
function move(cm, offset) {
|
|
pointer += offset;
|
|
if (pointer > head) {
|
|
pointer = head;
|
|
} else if (pointer < tail) {
|
|
pointer = tail;
|
|
}
|
|
var mark = buffer[(size + pointer) % size];
|
|
if (mark && !mark.find()) {
|
|
var inc = offset > 0 ? 1 : -1;
|
|
var newCur;
|
|
var oldCur = cm.getCursor();
|
|
do {
|
|
pointer += inc;
|
|
mark = buffer[(size + pointer) % size];
|
|
if (mark && (newCur = mark.find()) && !cursorEqual(oldCur, newCur)) {
|
|
break;
|
|
}
|
|
} while (pointer < head && pointer > tail);
|
|
}
|
|
return mark;
|
|
}
|
|
function find(cm, offset) {
|
|
var oldPointer = pointer;
|
|
var mark = move(cm, offset);
|
|
pointer = oldPointer;
|
|
return mark && mark.find();
|
|
}
|
|
return {
|
|
/**@type{Pos|undefined} */
|
|
cachedCursor: void 0,
|
|
//used for # and * jumps
|
|
add,
|
|
find,
|
|
move
|
|
};
|
|
};
|
|
var createInsertModeChanges = function(c) {
|
|
if (c) {
|
|
return {
|
|
changes: c.changes,
|
|
expectCursorActivityForChange: c.expectCursorActivityForChange
|
|
};
|
|
}
|
|
return {
|
|
// Change list
|
|
changes: [],
|
|
// Set to true on change, false on cursorActivity.
|
|
expectCursorActivityForChange: false
|
|
};
|
|
};
|
|
class MacroModeState {
|
|
constructor() {
|
|
this.latestRegister = void 0;
|
|
this.isPlaying = false;
|
|
this.isRecording = false;
|
|
this.replaySearchQueries = [];
|
|
this.onRecordingDone = void 0;
|
|
this.lastInsertModeChanges = createInsertModeChanges();
|
|
}
|
|
exitMacroRecordMode() {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
if (macroModeState.onRecordingDone) {
|
|
macroModeState.onRecordingDone();
|
|
}
|
|
macroModeState.onRecordingDone = void 0;
|
|
macroModeState.isRecording = false;
|
|
}
|
|
/**
|
|
* @arg {CodeMirror} cm
|
|
* @arg {string} registerName
|
|
*/
|
|
enterMacroRecordMode(cm, registerName) {
|
|
var register = vimGlobalState.registerController.getRegister(registerName);
|
|
if (register) {
|
|
register.clear();
|
|
this.latestRegister = registerName;
|
|
if (cm.openDialog) {
|
|
var template = dom("span", { class: "cm-vim-message" }, "recording @" + registerName);
|
|
this.onRecordingDone = cm.openDialog(template, function() {
|
|
}, { bottom: true });
|
|
}
|
|
this.isRecording = true;
|
|
}
|
|
}
|
|
}
|
|
function maybeInitVimState(cm) {
|
|
if (!cm.state.vim) {
|
|
cm.state.vim = {
|
|
inputState: new InputState(),
|
|
// Vim's input state that triggered the last edit, used to repeat
|
|
// motions and operators with '.'.
|
|
lastEditInputState: void 0,
|
|
// Vim's action command before the last edit, used to repeat actions
|
|
// with '.' and insert mode repeat.
|
|
lastEditActionCommand: void 0,
|
|
// When using jk for navigation, if you move from a longer line to a
|
|
// shorter line, the cursor may clip to the end of the shorter line.
|
|
// If j is pressed again and cursor goes to the next line, the
|
|
// cursor should go back to its horizontal position on the longer
|
|
// line if it can. This is to keep track of the horizontal position.
|
|
lastHPos: -1,
|
|
// Doing the same with screen-position for gj/gk
|
|
lastHSPos: -1,
|
|
// The last motion command run. Cleared if a non-motion command gets
|
|
// executed in between.
|
|
lastMotion: null,
|
|
marks: {},
|
|
insertMode: false,
|
|
insertModeReturn: false,
|
|
// Repeat count for changes made in insert mode, triggered by key
|
|
// sequences like 3,i. Only exists when insertMode is true.
|
|
insertModeRepeat: void 0,
|
|
visualMode: false,
|
|
// If we are in visual line mode. No effect if visualMode is false.
|
|
visualLine: false,
|
|
visualBlock: false,
|
|
lastSelection: (
|
|
/**@type{vimState["lastSelection"]}*/
|
|
/**@type{unknown}*/
|
|
null
|
|
),
|
|
lastPastedText: void 0,
|
|
sel: { anchor: new Pos2(0, 0), head: new Pos2(0, 0) },
|
|
// Buffer-local/window-local values of vim options.
|
|
options: {},
|
|
// Whether the next character should be interpreted literally
|
|
// Necassary for correct implementation of f<character>, r<character> etc.
|
|
// in terms of langmaps.
|
|
expectLiteralNext: false,
|
|
status: ""
|
|
};
|
|
}
|
|
return cm.state.vim;
|
|
}
|
|
var vimGlobalState;
|
|
function resetVimGlobalState() {
|
|
vimGlobalState = {
|
|
// The current search query.
|
|
searchQuery: null,
|
|
// Whether we are searching backwards.
|
|
searchIsReversed: false,
|
|
// Replace part of the last substituted pattern
|
|
lastSubstituteReplacePart: void 0,
|
|
jumpList: createCircularJumpList(),
|
|
macroModeState: new MacroModeState(),
|
|
// Recording latest f, t, F or T motion command.
|
|
lastCharacterSearch: { increment: 0, forward: true, selectedCharacter: "" },
|
|
registerController: new RegisterController({}),
|
|
// search history buffer
|
|
searchHistoryController: new HistoryController(),
|
|
// ex Command history buffer
|
|
exCommandHistoryController: new HistoryController()
|
|
};
|
|
for (var optionName in options) {
|
|
var option = options[optionName];
|
|
option.value = option.defaultValue;
|
|
}
|
|
}
|
|
class InsertModeKey {
|
|
/**
|
|
* Wrapper for special keys pressed in insert mode
|
|
* @arg {string} keyName
|
|
* @arg {KeyboardEvent} e
|
|
* @returns
|
|
*/
|
|
constructor(keyName, e) {
|
|
this.keyName = keyName;
|
|
this.key = e.key;
|
|
this.ctrlKey = e.ctrlKey;
|
|
this.altKey = e.altKey;
|
|
this.metaKey = e.metaKey;
|
|
this.shiftKey = e.shiftKey;
|
|
}
|
|
}
|
|
var lastInsertModeKeyTimer;
|
|
var vimApi = {
|
|
enterVimMode,
|
|
leaveVimMode,
|
|
buildKeyMap: function() {
|
|
},
|
|
// Testing hook, though it might be useful to expose the register
|
|
// controller anyway.
|
|
getRegisterController: function() {
|
|
return vimGlobalState.registerController;
|
|
},
|
|
// Testing hook.
|
|
resetVimGlobalState_: resetVimGlobalState,
|
|
// Testing hook.
|
|
getVimGlobalState_: function() {
|
|
return vimGlobalState;
|
|
},
|
|
// Testing hook.
|
|
maybeInitVimState_: maybeInitVimState,
|
|
suppressErrorLogging: false,
|
|
InsertModeKey,
|
|
/**@type {(lhs: string, rhs: string, ctx: string) => void} */
|
|
map: function(lhs, rhs, ctx) {
|
|
exCommandDispatcher.map(lhs, rhs, ctx);
|
|
},
|
|
/**@type {(lhs: string, ctx: string) => any} */
|
|
unmap: function(lhs, ctx) {
|
|
return exCommandDispatcher.unmap(lhs, ctx);
|
|
},
|
|
// Non-recursive map function.
|
|
// NOTE: This will not create mappings to key maps that aren't present
|
|
// in the default key map. See TODO at bottom of function.
|
|
/**@type {(lhs: string, rhs: string, ctx: string) => void} */
|
|
noremap: function(lhs, rhs, ctx) {
|
|
exCommandDispatcher.map(lhs, rhs, ctx, true);
|
|
},
|
|
// Remove all user-defined mappings for the provided context.
|
|
/**@arg {string} [ctx]} */
|
|
mapclear: function(ctx) {
|
|
var actualLength = defaultKeymap2.length, origLength = defaultKeymapLength;
|
|
var userKeymap = defaultKeymap2.slice(0, actualLength - origLength);
|
|
defaultKeymap2 = defaultKeymap2.slice(actualLength - origLength);
|
|
if (ctx) {
|
|
for (var i = userKeymap.length - 1; i >= 0; i--) {
|
|
var mapping = userKeymap[i];
|
|
if (ctx !== mapping.context) {
|
|
if (mapping.context) {
|
|
this._mapCommand(mapping);
|
|
} else {
|
|
var contexts = ["normal", "insert", "visual"];
|
|
for (var j in contexts) {
|
|
if (contexts[j] !== ctx) {
|
|
var newMapping = Object.assign({}, mapping);
|
|
newMapping.context = contexts[j];
|
|
this._mapCommand(newMapping);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
langmap: updateLangmap,
|
|
vimKeyFromEvent,
|
|
// TODO: Expose setOption and getOption as instance methods. Need to decide how to namespace
|
|
// them, or somehow make them work with the existing CodeMirror setOption/getOption API.
|
|
setOption,
|
|
getOption,
|
|
defineOption,
|
|
/**@type {(name: string, prefix: string|undefined, func: ExFn) => void} */
|
|
defineEx: function(name, prefix, func) {
|
|
if (!prefix) {
|
|
prefix = name;
|
|
} else if (name.indexOf(prefix) !== 0) {
|
|
throw new Error('(Vim.defineEx) "' + prefix + '" is not a prefix of "' + name + '", command not registered');
|
|
}
|
|
exCommands[name] = func;
|
|
exCommandDispatcher.commandMap_[prefix] = { name, shortName: prefix, type: "api" };
|
|
},
|
|
/**@type {(cm: CodeMirror, key: string, origin: string) => undefined | boolean} */
|
|
handleKey: function(cm, key, origin) {
|
|
var command = this.findKey(cm, key, origin);
|
|
if (typeof command === "function") {
|
|
return command();
|
|
}
|
|
},
|
|
multiSelectHandleKey,
|
|
/**
|
|
* This is the outermost function called by CodeMirror, after keys have
|
|
* been mapped to their Vim equivalents.
|
|
*
|
|
* Finds a command based on the key (and cached keys if there is a
|
|
* multi-key sequence). Returns `undefined` if no key is matched, a noop
|
|
* function if a partial match is found (multi-key), and a function to
|
|
* execute the bound command if a a key is matched. The function always
|
|
* returns true.
|
|
*/
|
|
/**@type {(cm_: CodeMirror, key: string, origin?: string| undefined) => (() => boolean|undefined) | undefined} */
|
|
findKey: function(cm_, key, origin) {
|
|
var vim2 = maybeInitVimState(cm_);
|
|
var cm = (
|
|
/**@type {CodeMirrorV}*/
|
|
cm_
|
|
);
|
|
function handleMacroRecording() {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
if (macroModeState.isRecording) {
|
|
if (key == "q") {
|
|
macroModeState.exitMacroRecordMode();
|
|
clearInputState(cm);
|
|
return true;
|
|
}
|
|
if (origin != "mapping") {
|
|
logKey(macroModeState, key);
|
|
}
|
|
}
|
|
}
|
|
function handleEsc() {
|
|
if (key == "<Esc>") {
|
|
if (vim2.visualMode) {
|
|
exitVisualMode(cm);
|
|
} else if (vim2.insertMode) {
|
|
exitInsertMode(cm);
|
|
} else {
|
|
return;
|
|
}
|
|
clearInputState(cm);
|
|
return true;
|
|
}
|
|
}
|
|
function handleKeyInsertMode() {
|
|
if (handleEsc()) {
|
|
return true;
|
|
}
|
|
vim2.inputState.keyBuffer.push(key);
|
|
var keys2 = vim2.inputState.keyBuffer.join("");
|
|
var keysAreChars = key.length == 1;
|
|
var match = commandDispatcher.matchCommand(keys2, defaultKeymap2, vim2.inputState, "insert");
|
|
var changeQueue = vim2.inputState.changeQueue;
|
|
if (match.type == "none") {
|
|
clearInputState(cm);
|
|
return false;
|
|
} else if (match.type == "partial") {
|
|
if (match.expectLiteralNext) vim2.expectLiteralNext = true;
|
|
if (lastInsertModeKeyTimer) {
|
|
window.clearTimeout(lastInsertModeKeyTimer);
|
|
}
|
|
lastInsertModeKeyTimer = keysAreChars && window.setTimeout(
|
|
function() {
|
|
if (vim2.insertMode && vim2.inputState.keyBuffer.length) {
|
|
clearInputState(cm);
|
|
}
|
|
},
|
|
getOption("insertModeEscKeysTimeout")
|
|
);
|
|
if (keysAreChars) {
|
|
var selections = cm.listSelections();
|
|
if (!changeQueue || changeQueue.removed.length != selections.length)
|
|
changeQueue = vim2.inputState.changeQueue = new ChangeQueue();
|
|
changeQueue.inserted += key;
|
|
for (var i = 0; i < selections.length; i++) {
|
|
var from = cursorMin(selections[i].anchor, selections[i].head);
|
|
var to = cursorMax(selections[i].anchor, selections[i].head);
|
|
var text = cm.getRange(from, cm.state.overwrite ? offsetCursor(to, 0, 1) : to);
|
|
changeQueue.removed[i] = (changeQueue.removed[i] || "") + text;
|
|
}
|
|
}
|
|
return !keysAreChars;
|
|
} else if (match.type == "full") {
|
|
vim2.inputState.keyBuffer.length = 0;
|
|
}
|
|
vim2.expectLiteralNext = false;
|
|
if (lastInsertModeKeyTimer) {
|
|
window.clearTimeout(lastInsertModeKeyTimer);
|
|
}
|
|
if (match.command && changeQueue) {
|
|
var selections = cm.listSelections();
|
|
for (var i = 0; i < selections.length; i++) {
|
|
var here = selections[i].head;
|
|
cm.replaceRange(
|
|
changeQueue.removed[i] || "",
|
|
offsetCursor(here, 0, -changeQueue.inserted.length),
|
|
here,
|
|
"+input"
|
|
);
|
|
}
|
|
vimGlobalState.macroModeState.lastInsertModeChanges.changes.pop();
|
|
}
|
|
if (!match.command) clearInputState(cm);
|
|
return match.command;
|
|
}
|
|
function handleKeyNonInsertMode() {
|
|
if (handleMacroRecording() || handleEsc()) {
|
|
return true;
|
|
}
|
|
vim2.inputState.keyBuffer.push(key);
|
|
var keys2 = vim2.inputState.keyBuffer.join("");
|
|
if (/^[1-9]\d*$/.test(keys2)) {
|
|
return true;
|
|
}
|
|
var keysMatcher = /^(\d*)(.*)$/.exec(keys2);
|
|
if (!keysMatcher) {
|
|
clearInputState(cm);
|
|
return false;
|
|
}
|
|
var context = vim2.visualMode ? "visual" : "normal";
|
|
var mainKey = keysMatcher[2] || keysMatcher[1];
|
|
if (vim2.inputState.operatorShortcut && vim2.inputState.operatorShortcut.slice(-1) == mainKey) {
|
|
mainKey = vim2.inputState.operatorShortcut;
|
|
}
|
|
var match = commandDispatcher.matchCommand(mainKey, defaultKeymap2, vim2.inputState, context);
|
|
if (match.type == "none") {
|
|
clearInputState(cm);
|
|
return false;
|
|
} else if (match.type == "partial") {
|
|
if (match.expectLiteralNext) vim2.expectLiteralNext = true;
|
|
return true;
|
|
} else if (match.type == "clear") {
|
|
clearInputState(cm);
|
|
return true;
|
|
}
|
|
vim2.expectLiteralNext = false;
|
|
vim2.inputState.keyBuffer.length = 0;
|
|
keysMatcher = /^(\d*)(.*)$/.exec(keys2);
|
|
if (keysMatcher && keysMatcher[1] && keysMatcher[1] != "0") {
|
|
vim2.inputState.pushRepeatDigit(keysMatcher[1]);
|
|
}
|
|
return match.command;
|
|
}
|
|
var command = vim2.insertMode ? handleKeyInsertMode() : handleKeyNonInsertMode();
|
|
if (command === false) {
|
|
return !vim2.insertMode && (key.length === 1 || CM.isMac && /<A-.>/.test(key)) ? function() {
|
|
return true;
|
|
} : void 0;
|
|
} else if (command === true) {
|
|
return function() {
|
|
return true;
|
|
};
|
|
} else if (command) {
|
|
return function() {
|
|
return cm.operation(function() {
|
|
cm.curOp.isVimOp = true;
|
|
try {
|
|
if (typeof command != "object") return;
|
|
if (command.type == "keyToKey") {
|
|
doKeyToKey(cm, command.toKeys, command);
|
|
} else {
|
|
commandDispatcher.processCommand(cm, vim2, command);
|
|
}
|
|
} catch (e) {
|
|
cm.state.vim = void 0;
|
|
maybeInitVimState(cm);
|
|
if (!vimApi.suppressErrorLogging) {
|
|
console["log"](e);
|
|
}
|
|
throw e;
|
|
}
|
|
return true;
|
|
});
|
|
};
|
|
}
|
|
},
|
|
/**@type {(cm: CodeMirrorV, input: string)=>void} */
|
|
handleEx: function(cm, input) {
|
|
exCommandDispatcher.processCommand(cm, input);
|
|
},
|
|
defineMotion,
|
|
defineAction,
|
|
defineOperator,
|
|
mapCommand,
|
|
_mapCommand,
|
|
defineRegister,
|
|
exitVisualMode,
|
|
exitInsertMode
|
|
};
|
|
var keyToKeyStack = [];
|
|
var noremap = false;
|
|
var virtualPrompt;
|
|
function sendKeyToPrompt(key) {
|
|
if (!virtualPrompt) throw new Error("No prompt to send key to");
|
|
if (key[0] == "<") {
|
|
var lowerKey = key.toLowerCase().slice(1, -1);
|
|
var parts = lowerKey.split("-");
|
|
lowerKey = parts.pop() || "";
|
|
if (lowerKey == "lt") key = "<";
|
|
else if (lowerKey == "space") key = " ";
|
|
else if (lowerKey == "cr") key = "\n";
|
|
else if (vimToCmKeyMap[lowerKey]) {
|
|
var value = virtualPrompt.value || "";
|
|
var event = {
|
|
key: vimToCmKeyMap[lowerKey],
|
|
target: {
|
|
value,
|
|
selectionEnd: value.length,
|
|
selectionStart: value.length
|
|
}
|
|
};
|
|
if (virtualPrompt.onKeyDown) {
|
|
virtualPrompt.onKeyDown(event, virtualPrompt.value, close);
|
|
}
|
|
if (virtualPrompt && virtualPrompt.onKeyUp) {
|
|
virtualPrompt.onKeyUp(event, virtualPrompt.value, close);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if (key == "\n") {
|
|
var prompt2 = virtualPrompt;
|
|
virtualPrompt = null;
|
|
prompt2.onClose && prompt2.onClose(prompt2.value);
|
|
} else {
|
|
virtualPrompt.value = (virtualPrompt.value || "") + key;
|
|
}
|
|
function close(value2) {
|
|
if (!virtualPrompt) return;
|
|
if (typeof value2 == "string") {
|
|
virtualPrompt.value = value2;
|
|
} else {
|
|
virtualPrompt = null;
|
|
}
|
|
}
|
|
}
|
|
function doKeyToKey(cm, keys2, fromKey) {
|
|
var noremapBefore = noremap;
|
|
if (fromKey) {
|
|
if (keyToKeyStack.indexOf(fromKey) != -1) return;
|
|
keyToKeyStack.push(fromKey);
|
|
noremap = fromKey.noremap != false;
|
|
}
|
|
try {
|
|
var vim2 = maybeInitVimState(cm);
|
|
var keyRe = /<(?:[CSMA]-)*\w+>|./gi;
|
|
var match;
|
|
while (match = keyRe.exec(keys2)) {
|
|
var key = match[0];
|
|
var wasInsert = vim2.insertMode;
|
|
if (virtualPrompt) {
|
|
sendKeyToPrompt(key);
|
|
continue;
|
|
}
|
|
var result = vimApi.handleKey(cm, key, "mapping");
|
|
if (!result && wasInsert && vim2.insertMode) {
|
|
if (key[0] == "<") {
|
|
var lowerKey = key.toLowerCase().slice(1, -1);
|
|
var parts = lowerKey.split("-");
|
|
lowerKey = parts.pop() || "";
|
|
if (lowerKey == "lt") key = "<";
|
|
else if (lowerKey == "space") key = " ";
|
|
else if (lowerKey == "cr") key = "\n";
|
|
else if (vimToCmKeyMap.hasOwnProperty(lowerKey)) {
|
|
key = vimToCmKeyMap[lowerKey];
|
|
sendCmKey(cm, key);
|
|
continue;
|
|
} else {
|
|
key = key[0];
|
|
keyRe.lastIndex = match.index + 1;
|
|
}
|
|
}
|
|
cm.replaceSelection(key);
|
|
}
|
|
}
|
|
} finally {
|
|
keyToKeyStack.pop();
|
|
noremap = keyToKeyStack.length ? noremapBefore : false;
|
|
if (!keyToKeyStack.length && virtualPrompt) {
|
|
var promptOptions = virtualPrompt;
|
|
virtualPrompt = null;
|
|
showPrompt(cm, promptOptions);
|
|
}
|
|
}
|
|
}
|
|
var specialKey = {
|
|
Return: "CR",
|
|
Backspace: "BS",
|
|
"Delete": "Del",
|
|
Escape: "Esc",
|
|
Insert: "Ins",
|
|
ArrowLeft: "Left",
|
|
ArrowRight: "Right",
|
|
ArrowUp: "Up",
|
|
ArrowDown: "Down",
|
|
Enter: "CR",
|
|
" ": "Space"
|
|
};
|
|
var ignoredKeys = {
|
|
Shift: 1,
|
|
Alt: 1,
|
|
Command: 1,
|
|
Control: 1,
|
|
CapsLock: 1,
|
|
AltGraph: 1,
|
|
Dead: 1,
|
|
Unidentified: 1
|
|
};
|
|
var vimToCmKeyMap = {};
|
|
"Left|Right|Up|Down|End|Home".split("|").concat(Object.keys(specialKey)).forEach(function(x) {
|
|
vimToCmKeyMap[(specialKey[x] || "").toLowerCase()] = vimToCmKeyMap[x.toLowerCase()] = x;
|
|
});
|
|
function vimKeyFromEvent(e, vim2) {
|
|
var _a;
|
|
var key = e.key;
|
|
if (ignoredKeys[key]) return;
|
|
if (key.length > 1 && key[0] == "n") {
|
|
key = key.replace("Numpad", "");
|
|
}
|
|
key = specialKey[key] || key;
|
|
var name = "";
|
|
if (e.ctrlKey) {
|
|
name += "C-";
|
|
}
|
|
if (e.altKey) {
|
|
name += "A-";
|
|
}
|
|
if (e.metaKey) {
|
|
name += "M-";
|
|
}
|
|
if (CM.isMac && name == "A-" && key.length == 1) {
|
|
name = name.slice(2);
|
|
}
|
|
if ((name || key.length > 1) && e.shiftKey) {
|
|
name += "S-";
|
|
}
|
|
if (vim2 && !vim2.expectLiteralNext && key.length == 1) {
|
|
if (langmap.keymap && key in langmap.keymap) {
|
|
if (langmap.remapCtrl != false || !name)
|
|
key = langmap.keymap[key];
|
|
} else if (key.charCodeAt(0) > 128) {
|
|
if (!usedKeys[key]) {
|
|
var code = ((_a = e.code) == null ? void 0 : _a.slice(-1)) || "";
|
|
if (!e.shiftKey) code = code.toLowerCase();
|
|
if (code) {
|
|
key = code;
|
|
if (!name && e.altKey) name = "A-";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
name += key;
|
|
if (name.length > 1) {
|
|
name = "<" + name + ">";
|
|
}
|
|
return name;
|
|
}
|
|
function updateLangmap(langmapString, remapCtrl) {
|
|
if (langmap.string !== langmapString) {
|
|
langmap = parseLangmap(langmapString);
|
|
}
|
|
langmap.remapCtrl = remapCtrl;
|
|
}
|
|
function parseLangmap(langmapString) {
|
|
let keymap3 = {};
|
|
if (!langmapString) return { keymap: keymap3, string: "" };
|
|
function getEscaped(list) {
|
|
return list.split(/\\?(.)/).filter(Boolean);
|
|
}
|
|
langmapString.split(/((?:[^\\,]|\\.)+),/).map((part) => {
|
|
if (!part) return;
|
|
const semicolon = part.split(/((?:[^\\;]|\\.)+);/);
|
|
if (semicolon.length == 3) {
|
|
const from = getEscaped(semicolon[1]);
|
|
const to = getEscaped(semicolon[2]);
|
|
if (from.length !== to.length) return;
|
|
for (let i = 0; i < from.length; ++i) keymap3[from[i]] = to[i];
|
|
} else if (semicolon.length == 1) {
|
|
const pairs = getEscaped(part);
|
|
if (pairs.length % 2 !== 0) return;
|
|
for (let i = 0; i < pairs.length; i += 2) keymap3[pairs[i]] = pairs[i + 1];
|
|
}
|
|
});
|
|
return { keymap: keymap3, string: langmapString };
|
|
}
|
|
defineOption("langmap", void 0, "string", ["lmap"], function(name, cm) {
|
|
if (name === void 0) {
|
|
return langmap.string;
|
|
} else {
|
|
updateLangmap(name);
|
|
}
|
|
});
|
|
class InputState {
|
|
constructor() {
|
|
this.prefixRepeat = [];
|
|
this.motionRepeat = [];
|
|
this.operator = null;
|
|
this.operatorArgs = null;
|
|
this.motion = null;
|
|
this.motionArgs = null;
|
|
this.keyBuffer = [];
|
|
this.registerName = void 0;
|
|
this.changeQueue = null;
|
|
}
|
|
/** @param {string} n */
|
|
pushRepeatDigit(n) {
|
|
if (!this.operator) {
|
|
this.prefixRepeat = this.prefixRepeat.concat(n);
|
|
} else {
|
|
this.motionRepeat = this.motionRepeat.concat(n);
|
|
}
|
|
}
|
|
getRepeat() {
|
|
var repeat = 0;
|
|
if (this.prefixRepeat.length > 0 || this.motionRepeat.length > 0) {
|
|
repeat = 1;
|
|
if (this.prefixRepeat.length > 0) {
|
|
repeat *= parseInt(this.prefixRepeat.join(""), 10);
|
|
}
|
|
if (this.motionRepeat.length > 0) {
|
|
repeat *= parseInt(this.motionRepeat.join(""), 10);
|
|
}
|
|
}
|
|
return repeat;
|
|
}
|
|
}
|
|
function clearInputState(cm, reason) {
|
|
cm.state.vim.inputState = new InputState();
|
|
cm.state.vim.expectLiteralNext = false;
|
|
CM.signal(cm, "vim-command-done", reason);
|
|
}
|
|
function ChangeQueue() {
|
|
this.removed = [];
|
|
this.inserted = "";
|
|
}
|
|
class Register {
|
|
/** @arg {string} [text] @arg {boolean} [linewise] @arg {boolean } [blockwise] */
|
|
constructor(text, linewise, blockwise) {
|
|
this.clear();
|
|
this.keyBuffer = [text || ""];
|
|
this.insertModeChanges = [];
|
|
this.searchQueries = [];
|
|
this.linewise = !!linewise;
|
|
this.blockwise = !!blockwise;
|
|
}
|
|
/** @arg {string} [text] @arg {boolean} [linewise] @arg {boolean } [blockwise] */
|
|
setText(text, linewise, blockwise) {
|
|
this.keyBuffer = [text || ""];
|
|
this.linewise = !!linewise;
|
|
this.blockwise = !!blockwise;
|
|
}
|
|
/** @arg {string} text @arg {boolean} [linewise] */
|
|
pushText(text, linewise) {
|
|
if (linewise) {
|
|
if (!this.linewise) {
|
|
this.keyBuffer.push("\n");
|
|
}
|
|
this.linewise = true;
|
|
}
|
|
this.keyBuffer.push(text);
|
|
}
|
|
/** @arg {InsertModeChanges} changes */
|
|
pushInsertModeChanges(changes) {
|
|
this.insertModeChanges.push(createInsertModeChanges(changes));
|
|
}
|
|
/** @arg {string} query */
|
|
pushSearchQuery(query) {
|
|
this.searchQueries.push(query);
|
|
}
|
|
clear() {
|
|
this.keyBuffer = [];
|
|
this.insertModeChanges = [];
|
|
this.searchQueries = [];
|
|
this.linewise = false;
|
|
}
|
|
toString() {
|
|
return this.keyBuffer.join("");
|
|
}
|
|
}
|
|
function defineRegister(name, register) {
|
|
var registers = vimGlobalState.registerController.registers;
|
|
if (!name || name.length != 1) {
|
|
throw Error("Register name must be 1 character");
|
|
}
|
|
if (registers[name]) {
|
|
throw Error("Register already defined " + name);
|
|
}
|
|
registers[name] = register;
|
|
validRegisters.push(name);
|
|
}
|
|
class RegisterController {
|
|
/** @arg {Object<string, Register>} registers */
|
|
constructor(registers) {
|
|
this.registers = registers;
|
|
this.unnamedRegister = registers['"'] = new Register();
|
|
registers["."] = new Register();
|
|
registers[":"] = new Register();
|
|
registers["/"] = new Register();
|
|
registers["+"] = new Register();
|
|
}
|
|
/**
|
|
* @param {string | null | undefined} registerName
|
|
* @param {string} operator
|
|
* @param {string} text
|
|
* @param {boolean} [linewise]
|
|
* @param {boolean} [blockwise]
|
|
*/
|
|
pushText(registerName, operator, text, linewise, blockwise) {
|
|
if (registerName === "_") return;
|
|
if (linewise && text.charAt(text.length - 1) !== "\n") {
|
|
text += "\n";
|
|
}
|
|
var register = this.isValidRegister(registerName) ? this.getRegister(registerName) : null;
|
|
if (!register || !registerName) {
|
|
switch (operator) {
|
|
case "yank":
|
|
this.registers["0"] = new Register(text, linewise, blockwise);
|
|
break;
|
|
case "delete":
|
|
case "change":
|
|
if (text.indexOf("\n") == -1) {
|
|
this.registers["-"] = new Register(text, linewise);
|
|
} else {
|
|
this.shiftNumericRegisters_();
|
|
this.registers["1"] = new Register(text, linewise);
|
|
}
|
|
break;
|
|
}
|
|
this.unnamedRegister.setText(text, linewise, blockwise);
|
|
return;
|
|
}
|
|
var append = isUpperCase(registerName);
|
|
if (append) {
|
|
register.pushText(text, linewise);
|
|
} else {
|
|
register.setText(text, linewise, blockwise);
|
|
}
|
|
if (registerName === "+") {
|
|
navigator.clipboard.writeText(text);
|
|
}
|
|
this.unnamedRegister.setText(register.toString(), linewise);
|
|
}
|
|
/**
|
|
* Gets the register named @name. If one of @name doesn't already exist,
|
|
* create it. If @name is invalid, return the unnamedRegister.
|
|
* @arg {string} [name]
|
|
*/
|
|
getRegister(name) {
|
|
if (!this.isValidRegister(name)) {
|
|
return this.unnamedRegister;
|
|
}
|
|
name = name.toLowerCase();
|
|
if (!this.registers[name]) {
|
|
this.registers[name] = new Register();
|
|
}
|
|
return this.registers[name];
|
|
}
|
|
/**@type {{(name: any): name is string}} */
|
|
isValidRegister(name) {
|
|
return name && (inArray(name, validRegisters) || latinCharRegex.test(name));
|
|
}
|
|
shiftNumericRegisters_() {
|
|
for (var i = 9; i >= 2; i--) {
|
|
this.registers[i] = this.getRegister("" + (i - 1));
|
|
}
|
|
}
|
|
}
|
|
class HistoryController {
|
|
constructor() {
|
|
this.historyBuffer = [];
|
|
this.iterator = 0;
|
|
this.initialPrefix = null;
|
|
}
|
|
/**
|
|
* the input argument here acts a user entered prefix for a small time
|
|
* until we start autocompletion in which case it is the autocompleted.
|
|
* @arg {string} input
|
|
* @arg {boolean} up
|
|
*/
|
|
nextMatch(input, up) {
|
|
var historyBuffer = this.historyBuffer;
|
|
var dir = up ? -1 : 1;
|
|
if (this.initialPrefix === null) this.initialPrefix = input;
|
|
for (var i = this.iterator + dir; up ? i >= 0 : i < historyBuffer.length; i += dir) {
|
|
var element = historyBuffer[i];
|
|
for (var j = 0; j <= element.length; j++) {
|
|
if (this.initialPrefix == element.substring(0, j)) {
|
|
this.iterator = i;
|
|
return element;
|
|
}
|
|
}
|
|
}
|
|
if (i >= historyBuffer.length) {
|
|
this.iterator = historyBuffer.length;
|
|
return this.initialPrefix;
|
|
}
|
|
if (i < 0) return input;
|
|
}
|
|
/** @arg {string} input */
|
|
pushInput(input) {
|
|
var index = this.historyBuffer.indexOf(input);
|
|
if (index > -1) this.historyBuffer.splice(index, 1);
|
|
if (input.length) this.historyBuffer.push(input);
|
|
}
|
|
reset() {
|
|
this.initialPrefix = null;
|
|
this.iterator = this.historyBuffer.length;
|
|
}
|
|
}
|
|
var commandDispatcher = {
|
|
/**
|
|
* @param {string} keys
|
|
* @param {vimKey[]} keyMap
|
|
* @param {InputStateInterface} inputState
|
|
* @param {string} context
|
|
*/
|
|
matchCommand: function(keys2, keyMap, inputState, context) {
|
|
var matches = commandMatches(keys2, keyMap, context, inputState);
|
|
var bestMatch = matches.full[0];
|
|
if (!bestMatch) {
|
|
if (matches.partial.length) {
|
|
return {
|
|
type: "partial",
|
|
expectLiteralNext: matches.partial.length == 1 && matches.partial[0].keys.slice(-11) == "<character>"
|
|
// langmap literal logic
|
|
};
|
|
}
|
|
return { type: "none" };
|
|
}
|
|
if (bestMatch.keys.slice(-11) == "<character>" || bestMatch.keys.slice(-10) == "<register>") {
|
|
var character = lastChar(keys2);
|
|
if (!character || character.length > 1) return { type: "clear" };
|
|
inputState.selectedCharacter = character;
|
|
}
|
|
return { type: "full", command: bestMatch };
|
|
},
|
|
/**
|
|
* @arg {CodeMirrorV} cm
|
|
* @arg {vimState} vim
|
|
* @arg {vimKey} command
|
|
*/
|
|
processCommand: function(cm, vim2, command) {
|
|
vim2.inputState.repeatOverride = command.repeatOverride;
|
|
switch (command.type) {
|
|
case "motion":
|
|
this.processMotion(cm, vim2, command);
|
|
break;
|
|
case "operator":
|
|
this.processOperator(cm, vim2, command);
|
|
break;
|
|
case "operatorMotion":
|
|
this.processOperatorMotion(cm, vim2, command);
|
|
break;
|
|
case "action":
|
|
this.processAction(cm, vim2, command);
|
|
break;
|
|
case "search":
|
|
this.processSearch(cm, vim2, command);
|
|
break;
|
|
case "ex":
|
|
case "keyToEx":
|
|
this.processEx(cm, vim2, command);
|
|
break;
|
|
}
|
|
},
|
|
/**
|
|
* @arg {CodeMirrorV} cm
|
|
* @arg {vimState} vim
|
|
* @arg {import("./types").motionCommand|import("./types").operatorMotionCommand} command
|
|
*/
|
|
processMotion: function(cm, vim2, command) {
|
|
vim2.inputState.motion = command.motion;
|
|
vim2.inputState.motionArgs = /**@type {MotionArgs}*/
|
|
copyArgs(command.motionArgs);
|
|
this.evalInput(cm, vim2);
|
|
},
|
|
/**
|
|
* @arg {CodeMirrorV} cm
|
|
* @arg {vimState} vim
|
|
* @arg {import("./types").operatorCommand|import("./types").operatorMotionCommand} command
|
|
*/
|
|
processOperator: function(cm, vim2, command) {
|
|
var inputState = vim2.inputState;
|
|
if (inputState.operator) {
|
|
if (inputState.operator == command.operator) {
|
|
inputState.motion = "expandToLine";
|
|
inputState.motionArgs = { linewise: true, repeat: 1 };
|
|
this.evalInput(cm, vim2);
|
|
return;
|
|
} else {
|
|
clearInputState(cm);
|
|
}
|
|
}
|
|
inputState.operator = command.operator;
|
|
inputState.operatorArgs = copyArgs(command.operatorArgs);
|
|
if (command.keys.length > 1) {
|
|
inputState.operatorShortcut = command.keys;
|
|
}
|
|
if (command.exitVisualBlock) {
|
|
vim2.visualBlock = false;
|
|
updateCmSelection(cm);
|
|
}
|
|
if (vim2.visualMode) {
|
|
this.evalInput(cm, vim2);
|
|
}
|
|
},
|
|
/**
|
|
* @arg {CodeMirrorV} cm
|
|
* @arg {vimState} vim
|
|
* @arg {import("./types").operatorMotionCommand} command
|
|
*/
|
|
processOperatorMotion: function(cm, vim2, command) {
|
|
var visualMode = vim2.visualMode;
|
|
var operatorMotionArgs = copyArgs(command.operatorMotionArgs);
|
|
if (operatorMotionArgs) {
|
|
if (visualMode && operatorMotionArgs.visualLine) {
|
|
vim2.visualLine = true;
|
|
}
|
|
}
|
|
this.processOperator(cm, vim2, command);
|
|
if (!visualMode) {
|
|
this.processMotion(cm, vim2, command);
|
|
}
|
|
},
|
|
/**
|
|
* @arg {CodeMirrorV} cm
|
|
* @arg {vimState} vim
|
|
* @arg {import("./types").actionCommand} command
|
|
*/
|
|
processAction: function(cm, vim2, command) {
|
|
var inputState = vim2.inputState;
|
|
var repeat = inputState.getRepeat();
|
|
var repeatIsExplicit = !!repeat;
|
|
var actionArgs = (
|
|
/**@type {ActionArgs}*/
|
|
copyArgs(command.actionArgs) || { repeat: 1 }
|
|
);
|
|
if (inputState.selectedCharacter) {
|
|
actionArgs.selectedCharacter = inputState.selectedCharacter;
|
|
}
|
|
if (command.operator) {
|
|
this.processOperator(cm, vim2, command);
|
|
}
|
|
if (command.motion) {
|
|
this.processMotion(cm, vim2, command);
|
|
}
|
|
if (command.motion || command.operator) {
|
|
this.evalInput(cm, vim2);
|
|
}
|
|
actionArgs.repeat = repeat || 1;
|
|
actionArgs.repeatIsExplicit = repeatIsExplicit;
|
|
actionArgs.registerName = inputState.registerName;
|
|
clearInputState(cm);
|
|
vim2.lastMotion = null;
|
|
if (command.isEdit) {
|
|
this.recordLastEdit(vim2, inputState, command);
|
|
}
|
|
actions[command.action](cm, actionArgs, vim2);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {vimState} vim @arg {import("./types").searchCommand} command*/
|
|
processSearch: function(cm, vim2, command) {
|
|
if (!cm.getSearchCursor) {
|
|
return;
|
|
}
|
|
var forward = command.searchArgs.forward;
|
|
var wholeWordOnly = command.searchArgs.wholeWordOnly;
|
|
getSearchState(cm).setReversed(!forward);
|
|
var promptPrefix = forward ? "/" : "?";
|
|
var originalQuery = getSearchState(cm).getQuery();
|
|
var originalScrollPos = cm.getScrollInfo();
|
|
var lastQuery = "";
|
|
function handleQuery(query, ignoreCase, smartCase) {
|
|
vimGlobalState.searchHistoryController.pushInput(query);
|
|
vimGlobalState.searchHistoryController.reset();
|
|
try {
|
|
updateSearchQuery(cm, query, ignoreCase, smartCase);
|
|
} catch (e) {
|
|
showConfirm(cm, "Invalid regex: " + query);
|
|
clearInputState(cm);
|
|
return;
|
|
}
|
|
commandDispatcher.processMotion(cm, vim2, {
|
|
keys: "",
|
|
type: "motion",
|
|
motion: "findNext",
|
|
motionArgs: { forward: true, toJumplist: command.searchArgs.toJumplist }
|
|
});
|
|
}
|
|
function onPromptClose(query) {
|
|
cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
|
|
handleQuery(
|
|
query,
|
|
true,
|
|
true
|
|
/** smartCase */
|
|
);
|
|
var macroModeState2 = vimGlobalState.macroModeState;
|
|
if (macroModeState2.isRecording) {
|
|
logSearchQuery(macroModeState2, query);
|
|
}
|
|
}
|
|
function pcreLabel() {
|
|
return getOption("pcre") ? "(JavaScript regexp: set pcre)" : "(Vim regexp: set nopcre)";
|
|
}
|
|
function onPromptKeyUp(e, query, close) {
|
|
var keyName = vimKeyFromEvent(e), up, offset;
|
|
if (keyName == "<Up>" || keyName == "<Down>") {
|
|
up = keyName == "<Up>" ? true : false;
|
|
offset = e.target ? e.target.selectionEnd : 0;
|
|
query = vimGlobalState.searchHistoryController.nextMatch(query, up) || "";
|
|
close(query);
|
|
if (offset && e.target) e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length);
|
|
} else if (keyName && keyName != "<Left>" && keyName != "<Right>") {
|
|
vimGlobalState.searchHistoryController.reset();
|
|
}
|
|
lastQuery = query;
|
|
onChange2();
|
|
}
|
|
function onChange2() {
|
|
var parsedQuery;
|
|
try {
|
|
parsedQuery = updateSearchQuery(
|
|
cm,
|
|
lastQuery,
|
|
true,
|
|
true
|
|
/** smartCase */
|
|
);
|
|
} catch (e) {
|
|
}
|
|
if (parsedQuery) {
|
|
cm.scrollIntoView(findNext(cm, !forward, parsedQuery), 30);
|
|
} else {
|
|
clearSearchHighlight(cm);
|
|
cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
|
|
}
|
|
}
|
|
function onPromptKeyDown(e, query, close) {
|
|
var keyName = vimKeyFromEvent(e);
|
|
if (keyName == "<Esc>" || keyName == "<C-c>" || keyName == "<C-[>" || keyName == "<BS>" && query == "") {
|
|
vimGlobalState.searchHistoryController.pushInput(query);
|
|
vimGlobalState.searchHistoryController.reset();
|
|
updateSearchQuery(cm, (originalQuery == null ? void 0 : originalQuery.source) || "");
|
|
clearSearchHighlight(cm);
|
|
cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
|
|
CM.e_stop(e);
|
|
clearInputState(cm);
|
|
close();
|
|
cm.focus();
|
|
} else if (keyName == "<Up>" || keyName == "<Down>") {
|
|
CM.e_stop(e);
|
|
} else if (keyName == "<C-u>") {
|
|
CM.e_stop(e);
|
|
close("");
|
|
}
|
|
}
|
|
switch (command.searchArgs.querySrc) {
|
|
case "prompt":
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
if (macroModeState.isPlaying) {
|
|
let query2 = macroModeState.replaySearchQueries.shift();
|
|
handleQuery(
|
|
query2 || "",
|
|
true,
|
|
false
|
|
/** smartCase */
|
|
);
|
|
} else {
|
|
showPrompt(cm, {
|
|
onClose: onPromptClose,
|
|
prefix: promptPrefix,
|
|
desc: dom(
|
|
"span",
|
|
{
|
|
$cursor: "pointer",
|
|
onmousedown: function(e) {
|
|
e.preventDefault();
|
|
setOption("pcre", !getOption("pcre"));
|
|
this.textContent = pcreLabel();
|
|
onChange2();
|
|
}
|
|
},
|
|
pcreLabel()
|
|
),
|
|
onKeyUp: onPromptKeyUp,
|
|
onKeyDown: onPromptKeyDown
|
|
});
|
|
}
|
|
break;
|
|
case "wordUnderCursor":
|
|
var word = expandWordUnderCursor(cm, { noSymbol: true });
|
|
var isKeyword = true;
|
|
if (!word) {
|
|
word = expandWordUnderCursor(cm, { noSymbol: false });
|
|
isKeyword = false;
|
|
}
|
|
if (!word) {
|
|
showConfirm(cm, "No word under cursor");
|
|
clearInputState(cm);
|
|
return;
|
|
}
|
|
let query = cm.getLine(word.start.line).substring(
|
|
word.start.ch,
|
|
word.end.ch
|
|
);
|
|
if (isKeyword && wholeWordOnly) {
|
|
query = "\\b" + query + "\\b";
|
|
} else {
|
|
query = escapeRegex(query);
|
|
}
|
|
vimGlobalState.jumpList.cachedCursor = cm.getCursor();
|
|
cm.setCursor(word.start);
|
|
handleQuery(
|
|
query,
|
|
true,
|
|
false
|
|
/** smartCase */
|
|
);
|
|
break;
|
|
}
|
|
},
|
|
/**
|
|
* @arg {CodeMirrorV} cm
|
|
* @arg {vimState} vim
|
|
* @arg {import("./types").exCommand | import("./types").keyToExCommand} command
|
|
*/
|
|
processEx: function(cm, vim2, command) {
|
|
function onPromptClose(input) {
|
|
vimGlobalState.exCommandHistoryController.pushInput(input);
|
|
vimGlobalState.exCommandHistoryController.reset();
|
|
exCommandDispatcher.processCommand(cm, input);
|
|
if (cm.state.vim) clearInputState(cm);
|
|
clearSearchHighlight(cm);
|
|
}
|
|
function onPromptKeyDown(e, input, close) {
|
|
var keyName = vimKeyFromEvent(e), up, offset;
|
|
if (keyName == "<Esc>" || keyName == "<C-c>" || keyName == "<C-[>" || keyName == "<BS>" && input == "") {
|
|
vimGlobalState.exCommandHistoryController.pushInput(input);
|
|
vimGlobalState.exCommandHistoryController.reset();
|
|
CM.e_stop(e);
|
|
clearInputState(cm);
|
|
clearSearchHighlight(cm);
|
|
close();
|
|
cm.focus();
|
|
}
|
|
if (keyName == "<Up>" || keyName == "<Down>") {
|
|
CM.e_stop(e);
|
|
up = keyName == "<Up>" ? true : false;
|
|
offset = e.target ? e.target.selectionEnd : 0;
|
|
input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || "";
|
|
close(input);
|
|
if (offset && e.target) e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length);
|
|
} else if (keyName == "<C-u>") {
|
|
CM.e_stop(e);
|
|
close("");
|
|
} else if (keyName && keyName != "<Left>" && keyName != "<Right>") {
|
|
vimGlobalState.exCommandHistoryController.reset();
|
|
}
|
|
}
|
|
function onPromptKeyUp(e, query) {
|
|
var inputStream = new CM.StringStream(query);
|
|
var params = (
|
|
/**@type{import("./types").exCommandArgs}*/
|
|
{}
|
|
);
|
|
try {
|
|
exCommandDispatcher.parseInput_(cm, inputStream, params);
|
|
if (params.commandName != "s") {
|
|
clearSearchHighlight(cm);
|
|
return;
|
|
}
|
|
var command2 = exCommandDispatcher.matchCommand_(params.commandName);
|
|
if (!command2) return;
|
|
exCommandDispatcher.parseCommandArgs_(inputStream, params, command2);
|
|
if (!params.argString) return;
|
|
var regex = parseQuery(params.argString.slice(1), true, true);
|
|
if (regex) highlightSearchMatches(cm, regex);
|
|
} catch (e2) {
|
|
}
|
|
}
|
|
if (command.type == "keyToEx") {
|
|
exCommandDispatcher.processCommand(cm, command.exArgs.input);
|
|
} else {
|
|
var promptOptions = {
|
|
onClose: onPromptClose,
|
|
onKeyDown: onPromptKeyDown,
|
|
onKeyUp: onPromptKeyUp,
|
|
prefix: ":"
|
|
};
|
|
if (vim2.visualMode) {
|
|
promptOptions.value = "'<,'>";
|
|
promptOptions.selectValueOnOpen = false;
|
|
}
|
|
showPrompt(cm, promptOptions);
|
|
}
|
|
},
|
|
/**@arg {CodeMirrorV} cm @arg {vimState} vim */
|
|
evalInput: function(cm, vim2) {
|
|
var inputState = vim2.inputState;
|
|
var motion = inputState.motion;
|
|
var motionArgs = inputState.motionArgs || { repeat: 1 };
|
|
var operator = inputState.operator;
|
|
var operatorArgs = inputState.operatorArgs || {};
|
|
var registerName = inputState.registerName;
|
|
var sel = vim2.sel;
|
|
var origHead = copyCursor(vim2.visualMode ? clipCursorToContent(cm, sel.head) : cm.getCursor("head"));
|
|
var origAnchor = copyCursor(vim2.visualMode ? clipCursorToContent(cm, sel.anchor) : cm.getCursor("anchor"));
|
|
var oldHead = copyCursor(origHead);
|
|
var oldAnchor = copyCursor(origAnchor);
|
|
var newHead, newAnchor;
|
|
var repeat;
|
|
if (operator) {
|
|
this.recordLastEdit(vim2, inputState);
|
|
}
|
|
if (inputState.repeatOverride !== void 0) {
|
|
repeat = inputState.repeatOverride;
|
|
} else {
|
|
repeat = inputState.getRepeat();
|
|
}
|
|
if (repeat > 0 && motionArgs.explicitRepeat) {
|
|
motionArgs.repeatIsExplicit = true;
|
|
} else if (motionArgs.noRepeat || !motionArgs.explicitRepeat && repeat === 0) {
|
|
repeat = 1;
|
|
motionArgs.repeatIsExplicit = false;
|
|
}
|
|
if (inputState.selectedCharacter) {
|
|
motionArgs.selectedCharacter = operatorArgs.selectedCharacter = inputState.selectedCharacter;
|
|
}
|
|
motionArgs.repeat = repeat;
|
|
clearInputState(cm);
|
|
if (motion) {
|
|
var motionResult = motions[motion](cm, origHead, motionArgs, vim2, inputState);
|
|
vim2.lastMotion = motions[motion];
|
|
if (!motionResult) {
|
|
return;
|
|
}
|
|
if (motionArgs.toJumplist) {
|
|
var jumpList = vimGlobalState.jumpList;
|
|
var cachedCursor = jumpList.cachedCursor;
|
|
if (cachedCursor) {
|
|
recordJumpPosition(cm, cachedCursor, motionResult);
|
|
delete jumpList.cachedCursor;
|
|
} else {
|
|
recordJumpPosition(cm, origHead, motionResult);
|
|
}
|
|
}
|
|
if (motionResult instanceof Array) {
|
|
newAnchor = motionResult[0];
|
|
newHead = motionResult[1];
|
|
} else {
|
|
newHead = motionResult;
|
|
}
|
|
if (!newHead) {
|
|
newHead = copyCursor(origHead);
|
|
}
|
|
if (vim2.visualMode) {
|
|
if (!(vim2.visualBlock && newHead.ch === Infinity)) {
|
|
newHead = clipCursorToContent(cm, newHead, oldHead);
|
|
}
|
|
if (newAnchor) {
|
|
newAnchor = clipCursorToContent(cm, newAnchor);
|
|
}
|
|
newAnchor = newAnchor || oldAnchor;
|
|
sel.anchor = newAnchor;
|
|
sel.head = newHead;
|
|
updateCmSelection(cm);
|
|
updateMark(
|
|
cm,
|
|
vim2,
|
|
"<",
|
|
cursorIsBefore(newAnchor, newHead) ? newAnchor : newHead
|
|
);
|
|
updateMark(
|
|
cm,
|
|
vim2,
|
|
">",
|
|
cursorIsBefore(newAnchor, newHead) ? newHead : newAnchor
|
|
);
|
|
} else if (!operator) {
|
|
newHead = clipCursorToContent(cm, newHead, oldHead);
|
|
cm.setCursor(newHead.line, newHead.ch);
|
|
}
|
|
}
|
|
if (operator) {
|
|
if (operatorArgs.lastSel) {
|
|
newAnchor = oldAnchor;
|
|
var lastSel = operatorArgs.lastSel;
|
|
var lineOffset = Math.abs(lastSel.head.line - lastSel.anchor.line);
|
|
var chOffset = Math.abs(lastSel.head.ch - lastSel.anchor.ch);
|
|
if (lastSel.visualLine) {
|
|
newHead = new Pos2(oldAnchor.line + lineOffset, oldAnchor.ch);
|
|
} else if (lastSel.visualBlock) {
|
|
newHead = new Pos2(oldAnchor.line + lineOffset, oldAnchor.ch + chOffset);
|
|
} else if (lastSel.head.line == lastSel.anchor.line) {
|
|
newHead = new Pos2(oldAnchor.line, oldAnchor.ch + chOffset);
|
|
} else {
|
|
newHead = new Pos2(oldAnchor.line + lineOffset, oldAnchor.ch);
|
|
}
|
|
vim2.visualMode = true;
|
|
vim2.visualLine = lastSel.visualLine;
|
|
vim2.visualBlock = lastSel.visualBlock;
|
|
sel = vim2.sel = {
|
|
anchor: newAnchor,
|
|
head: newHead
|
|
};
|
|
updateCmSelection(cm);
|
|
} else if (vim2.visualMode) {
|
|
operatorArgs.lastSel = {
|
|
anchor: copyCursor(sel.anchor),
|
|
head: copyCursor(sel.head),
|
|
visualBlock: vim2.visualBlock,
|
|
visualLine: vim2.visualLine
|
|
};
|
|
}
|
|
var curStart, curEnd, linewise;
|
|
var mode;
|
|
var cmSel;
|
|
if (vim2.visualMode) {
|
|
curStart = cursorMin(sel.head, sel.anchor);
|
|
curEnd = cursorMax(sel.head, sel.anchor);
|
|
linewise = vim2.visualLine || operatorArgs.linewise;
|
|
mode = vim2.visualBlock ? "block" : linewise ? "line" : "char";
|
|
var newPositions = updateSelectionForSurrogateCharacters(cm, curStart, curEnd);
|
|
cmSel = makeCmSelection(cm, {
|
|
anchor: newPositions.start,
|
|
head: newPositions.end
|
|
}, mode);
|
|
if (linewise) {
|
|
var ranges = cmSel.ranges;
|
|
if (mode == "block") {
|
|
for (var i = 0; i < ranges.length; i++) {
|
|
ranges[i].head.ch = lineLength(cm, ranges[i].head.line);
|
|
}
|
|
} else if (mode == "line") {
|
|
ranges[0].head = new Pos2(ranges[0].head.line + 1, 0);
|
|
}
|
|
}
|
|
} else {
|
|
curStart = copyCursor(newAnchor || oldAnchor);
|
|
curEnd = copyCursor(newHead || oldHead);
|
|
if (cursorIsBefore(curEnd, curStart)) {
|
|
var tmp = curStart;
|
|
curStart = curEnd;
|
|
curEnd = tmp;
|
|
}
|
|
linewise = motionArgs.linewise || operatorArgs.linewise;
|
|
if (linewise) {
|
|
expandSelectionToLine(cm, curStart, curEnd);
|
|
} else if (motionArgs.forward) {
|
|
clipToLine(cm, curStart, curEnd);
|
|
}
|
|
mode = "char";
|
|
var exclusive = !motionArgs.inclusive || linewise;
|
|
var newPositions = updateSelectionForSurrogateCharacters(cm, curStart, curEnd);
|
|
cmSel = makeCmSelection(cm, {
|
|
anchor: newPositions.start,
|
|
head: newPositions.end
|
|
}, mode, exclusive);
|
|
}
|
|
cm.setSelections(cmSel.ranges, cmSel.primary);
|
|
vim2.lastMotion = null;
|
|
operatorArgs.repeat = repeat;
|
|
operatorArgs.registerName = registerName;
|
|
operatorArgs.linewise = linewise;
|
|
var operatorMoveTo = operators[operator](
|
|
cm,
|
|
operatorArgs,
|
|
cmSel.ranges,
|
|
oldAnchor,
|
|
newHead
|
|
);
|
|
if (vim2.visualMode) {
|
|
exitVisualMode(cm, operatorMoveTo != null);
|
|
}
|
|
if (operatorMoveTo) {
|
|
cm.setCursor(operatorMoveTo);
|
|
}
|
|
}
|
|
},
|
|
/**@arg {vimState} vim @arg {InputStateInterface} inputState, @arg {import("./types").actionCommand} [actionCommand] */
|
|
recordLastEdit: function(vim2, inputState, actionCommand) {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
if (macroModeState.isPlaying) {
|
|
return;
|
|
}
|
|
vim2.lastEditInputState = inputState;
|
|
vim2.lastEditActionCommand = actionCommand;
|
|
macroModeState.lastInsertModeChanges.changes = [];
|
|
macroModeState.lastInsertModeChanges.expectCursorActivityForChange = false;
|
|
macroModeState.lastInsertModeChanges.visualBlock = vim2.visualBlock ? vim2.sel.head.line - vim2.sel.anchor.line : 0;
|
|
}
|
|
};
|
|
var motions = {
|
|
moveToTopLine: function(cm, _head, motionArgs) {
|
|
var line = getUserVisibleLines(cm).top + motionArgs.repeat - 1;
|
|
return new Pos2(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
|
|
},
|
|
moveToMiddleLine: function(cm) {
|
|
var range = getUserVisibleLines(cm);
|
|
var line = Math.floor((range.top + range.bottom) * 0.5);
|
|
return new Pos2(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
|
|
},
|
|
moveToBottomLine: function(cm, _head, motionArgs) {
|
|
var line = getUserVisibleLines(cm).bottom - motionArgs.repeat + 1;
|
|
return new Pos2(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
|
|
},
|
|
expandToLine: function(_cm, head, motionArgs) {
|
|
var cur2 = head;
|
|
return new Pos2(cur2.line + motionArgs.repeat - 1, Infinity);
|
|
},
|
|
findNext: function(cm, _head, motionArgs) {
|
|
var state = getSearchState(cm);
|
|
var query = state.getQuery();
|
|
if (!query) {
|
|
return;
|
|
}
|
|
var prev = !motionArgs.forward;
|
|
prev = state.isReversed() ? !prev : prev;
|
|
highlightSearchMatches(cm, query);
|
|
var result = findNext(cm, prev, query, motionArgs.repeat);
|
|
if (!result) {
|
|
showConfirm(cm, "No match found " + query + (getOption("pcre") ? " (set nopcre to use Vim regexps)" : ""));
|
|
}
|
|
return result;
|
|
},
|
|
/**
|
|
* Find and select the next occurrence of the search query. If the cursor is currently
|
|
* within a match, then find and select the current match. Otherwise, find the next occurrence in the
|
|
* appropriate direction.
|
|
*
|
|
* This differs from `findNext` in the following ways:
|
|
*
|
|
* 1. Instead of only returning the "from", this returns a "from", "to" range.
|
|
* 2. If the cursor is currently inside a search match, this selects the current match
|
|
* instead of the next match.
|
|
* 3. If there is no associated operator, this will turn on visual mode.
|
|
*/
|
|
findAndSelectNextInclusive: function(cm, _head, motionArgs, vim2, prevInputState) {
|
|
var state = getSearchState(cm);
|
|
var query = state.getQuery();
|
|
if (!query) {
|
|
return;
|
|
}
|
|
var prev = !motionArgs.forward;
|
|
prev = state.isReversed() ? !prev : prev;
|
|
var next = findNextFromAndToInclusive(cm, prev, query, motionArgs.repeat, vim2);
|
|
if (!next) {
|
|
return;
|
|
}
|
|
if (prevInputState.operator) {
|
|
return next;
|
|
}
|
|
var from = next[0];
|
|
var to = new Pos2(next[1].line, next[1].ch - 1);
|
|
if (vim2.visualMode) {
|
|
if (vim2.visualLine || vim2.visualBlock) {
|
|
vim2.visualLine = false;
|
|
vim2.visualBlock = false;
|
|
CM.signal(cm, "vim-mode-change", { mode: "visual", subMode: "" });
|
|
}
|
|
var anchor = vim2.sel.anchor;
|
|
if (anchor) {
|
|
if (state.isReversed()) {
|
|
if (motionArgs.forward) {
|
|
return [anchor, from];
|
|
}
|
|
return [anchor, to];
|
|
} else {
|
|
if (motionArgs.forward) {
|
|
return [anchor, to];
|
|
}
|
|
return [anchor, from];
|
|
}
|
|
}
|
|
} else {
|
|
vim2.visualMode = true;
|
|
vim2.visualLine = false;
|
|
vim2.visualBlock = false;
|
|
CM.signal(cm, "vim-mode-change", { mode: "visual", subMode: "" });
|
|
}
|
|
return prev ? [to, from] : [from, to];
|
|
},
|
|
goToMark: function(cm, _head, motionArgs, vim2) {
|
|
var pos = getMarkPos(cm, vim2, motionArgs.selectedCharacter || "");
|
|
if (pos) {
|
|
return motionArgs.linewise ? { line: pos.line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(pos.line)) } : pos;
|
|
}
|
|
return null;
|
|
},
|
|
moveToOtherHighlightedEnd: function(cm, _head, motionArgs, vim2) {
|
|
var sel = vim2.sel;
|
|
if (vim2.visualBlock && motionArgs.sameLine) {
|
|
return [
|
|
clipCursorToContent(cm, new Pos2(sel.anchor.line, sel.head.ch)),
|
|
clipCursorToContent(cm, new Pos2(sel.head.line, sel.anchor.ch))
|
|
];
|
|
} else {
|
|
return [sel.head, sel.anchor];
|
|
}
|
|
},
|
|
jumpToMark: function(cm, head, motionArgs, vim2) {
|
|
var best = head;
|
|
for (var i = 0; i < motionArgs.repeat; i++) {
|
|
var cursor = best;
|
|
for (var key in vim2.marks) {
|
|
if (!isLowerCase(key)) {
|
|
continue;
|
|
}
|
|
var mark = vim2.marks[key].find();
|
|
var isWrongDirection = motionArgs.forward ? (
|
|
// @ts-ignore
|
|
cursorIsBefore(mark, cursor)
|
|
) : cursorIsBefore(cursor, mark);
|
|
if (isWrongDirection) {
|
|
continue;
|
|
}
|
|
if (motionArgs.linewise && mark.line == cursor.line) {
|
|
continue;
|
|
}
|
|
var equal = cursorEqual(cursor, best);
|
|
var between = motionArgs.forward ? (
|
|
// @ts-ignore
|
|
cursorIsBetween(cursor, mark, best)
|
|
) : (
|
|
// @ts-ignore
|
|
cursorIsBetween(best, mark, cursor)
|
|
);
|
|
if (equal || between) {
|
|
best = mark;
|
|
}
|
|
}
|
|
}
|
|
if (motionArgs.linewise) {
|
|
best = new Pos2(best.line, findFirstNonWhiteSpaceCharacter(cm.getLine(best.line)));
|
|
}
|
|
return best;
|
|
},
|
|
moveByCharacters: function(_cm, head, motionArgs) {
|
|
var cur2 = head;
|
|
var repeat = motionArgs.repeat;
|
|
var ch = motionArgs.forward ? cur2.ch + repeat : cur2.ch - repeat;
|
|
return new Pos2(cur2.line, ch);
|
|
},
|
|
moveByLines: function(cm, head, motionArgs, vim2) {
|
|
var cur2 = head;
|
|
var endCh = cur2.ch;
|
|
switch (vim2.lastMotion) {
|
|
case this.moveByLines:
|
|
case this.moveByDisplayLines:
|
|
case this.moveByScroll:
|
|
case this.moveToColumn:
|
|
case this.moveToEol:
|
|
endCh = vim2.lastHPos;
|
|
break;
|
|
default:
|
|
vim2.lastHPos = endCh;
|
|
}
|
|
var repeat = motionArgs.repeat + (motionArgs.repeatOffset || 0);
|
|
var line = motionArgs.forward ? cur2.line + repeat : cur2.line - repeat;
|
|
var first = cm.firstLine();
|
|
var last = cm.lastLine();
|
|
var posV = cm.findPosV(cur2, motionArgs.forward ? repeat : -repeat, "line", vim2.lastHSPos);
|
|
var hasMarkedText = motionArgs.forward ? posV.line > line : posV.line < line;
|
|
if (hasMarkedText) {
|
|
line = posV.line;
|
|
endCh = posV.ch;
|
|
}
|
|
if (line < first && cur2.line == first) {
|
|
return this.moveToStartOfLine(cm, head, motionArgs, vim2);
|
|
} else if (line > last && cur2.line == last) {
|
|
return moveToEol(cm, head, motionArgs, vim2, true);
|
|
}
|
|
if (motionArgs.toFirstChar) {
|
|
endCh = findFirstNonWhiteSpaceCharacter(cm.getLine(line));
|
|
vim2.lastHPos = endCh;
|
|
}
|
|
vim2.lastHSPos = cm.charCoords(new Pos2(line, endCh), "div").left;
|
|
return new Pos2(line, endCh);
|
|
},
|
|
moveByDisplayLines: function(cm, head, motionArgs, vim2) {
|
|
var cur2 = head;
|
|
switch (vim2.lastMotion) {
|
|
case this.moveByDisplayLines:
|
|
case this.moveByScroll:
|
|
case this.moveByLines:
|
|
case this.moveToColumn:
|
|
case this.moveToEol:
|
|
break;
|
|
default:
|
|
vim2.lastHSPos = cm.charCoords(cur2, "div").left;
|
|
}
|
|
var repeat = motionArgs.repeat;
|
|
var res = cm.findPosV(cur2, motionArgs.forward ? repeat : -repeat, "line", vim2.lastHSPos);
|
|
if (res.hitSide) {
|
|
if (motionArgs.forward) {
|
|
var lastCharCoords = cm.charCoords(res, "div");
|
|
var goalCoords = { top: lastCharCoords.top + 8, left: vim2.lastHSPos };
|
|
res = cm.coordsChar(goalCoords, "div");
|
|
} else {
|
|
var resCoords = cm.charCoords(new Pos2(cm.firstLine(), 0), "div");
|
|
resCoords.left = vim2.lastHSPos;
|
|
res = cm.coordsChar(resCoords, "div");
|
|
}
|
|
}
|
|
vim2.lastHPos = res.ch;
|
|
return res;
|
|
},
|
|
moveByPage: function(cm, head, motionArgs) {
|
|
var curStart = head;
|
|
var repeat = motionArgs.repeat;
|
|
return cm.findPosV(curStart, motionArgs.forward ? repeat : -repeat, "page");
|
|
},
|
|
moveByParagraph: function(cm, head, motionArgs) {
|
|
var dir = motionArgs.forward ? 1 : -1;
|
|
return findParagraph(cm, head, motionArgs.repeat, dir).start;
|
|
},
|
|
moveBySentence: function(cm, head, motionArgs) {
|
|
var dir = motionArgs.forward ? 1 : -1;
|
|
return findSentence(cm, head, motionArgs.repeat, dir);
|
|
},
|
|
moveByScroll: function(cm, head, motionArgs, vim2) {
|
|
var scrollbox = cm.getScrollInfo();
|
|
var curEnd = null;
|
|
var repeat = motionArgs.repeat;
|
|
if (!repeat) {
|
|
repeat = scrollbox.clientHeight / (2 * cm.defaultTextHeight());
|
|
}
|
|
var orig = cm.charCoords(head, "local");
|
|
motionArgs.repeat = repeat;
|
|
curEnd = motions.moveByDisplayLines(cm, head, motionArgs, vim2);
|
|
if (!curEnd) {
|
|
return null;
|
|
}
|
|
var dest = cm.charCoords(curEnd, "local");
|
|
cm.scrollTo(null, scrollbox.top + dest.top - orig.top);
|
|
return curEnd;
|
|
},
|
|
moveByWords: function(cm, head, motionArgs) {
|
|
return moveToWord(
|
|
cm,
|
|
head,
|
|
motionArgs.repeat,
|
|
!!motionArgs.forward,
|
|
!!motionArgs.wordEnd,
|
|
!!motionArgs.bigWord
|
|
);
|
|
},
|
|
moveTillCharacter: function(cm, head, motionArgs) {
|
|
var repeat = motionArgs.repeat;
|
|
var curEnd = moveToCharacter(
|
|
cm,
|
|
repeat,
|
|
motionArgs.forward,
|
|
motionArgs.selectedCharacter,
|
|
head
|
|
);
|
|
var increment = motionArgs.forward ? -1 : 1;
|
|
recordLastCharacterSearch(increment, motionArgs);
|
|
if (!curEnd) return null;
|
|
curEnd.ch += increment;
|
|
return curEnd;
|
|
},
|
|
moveToCharacter: function(cm, head, motionArgs) {
|
|
var repeat = motionArgs.repeat;
|
|
recordLastCharacterSearch(0, motionArgs);
|
|
return moveToCharacter(
|
|
cm,
|
|
repeat,
|
|
motionArgs.forward,
|
|
motionArgs.selectedCharacter,
|
|
head
|
|
) || head;
|
|
},
|
|
moveToSymbol: function(cm, head, motionArgs) {
|
|
var repeat = motionArgs.repeat;
|
|
return motionArgs.selectedCharacter && findSymbol(
|
|
cm,
|
|
repeat,
|
|
motionArgs.forward,
|
|
motionArgs.selectedCharacter
|
|
) || head;
|
|
},
|
|
moveToColumn: function(cm, head, motionArgs, vim2) {
|
|
var repeat = motionArgs.repeat;
|
|
vim2.lastHPos = repeat - 1;
|
|
vim2.lastHSPos = cm.charCoords(head, "div").left;
|
|
return moveToColumn(cm, repeat);
|
|
},
|
|
moveToEol: function(cm, head, motionArgs, vim2) {
|
|
return moveToEol(cm, head, motionArgs, vim2, false);
|
|
},
|
|
moveToFirstNonWhiteSpaceCharacter: function(cm, head) {
|
|
var cursor = head;
|
|
return new Pos2(
|
|
cursor.line,
|
|
findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line))
|
|
);
|
|
},
|
|
moveToMatchedSymbol: function(cm, head) {
|
|
var cursor = head;
|
|
var line = cursor.line;
|
|
var ch = cursor.ch;
|
|
var lineText = cm.getLine(line);
|
|
var symbol;
|
|
for (; ch < lineText.length; ch++) {
|
|
symbol = lineText.charAt(ch);
|
|
if (symbol && isMatchableSymbol(symbol)) {
|
|
var style = cm.getTokenTypeAt(new Pos2(line, ch + 1));
|
|
if (style !== "string" && style !== "comment") {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (ch < lineText.length) {
|
|
var re = symbol === "<" || symbol === ">" ? /[(){}[\]<>]/ : /[(){}[\]]/;
|
|
var matched = cm.findMatchingBracket(new Pos2(line, ch), { bracketRegex: re });
|
|
return matched.to;
|
|
} else {
|
|
return cursor;
|
|
}
|
|
},
|
|
moveToStartOfLine: function(_cm, head) {
|
|
return new Pos2(head.line, 0);
|
|
},
|
|
moveToLineOrEdgeOfDocument: function(cm, _head, motionArgs) {
|
|
var lineNum = motionArgs.forward ? cm.lastLine() : cm.firstLine();
|
|
if (motionArgs.repeatIsExplicit) {
|
|
lineNum = motionArgs.repeat - cm.getOption("firstLineNumber");
|
|
}
|
|
return new Pos2(
|
|
lineNum,
|
|
findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum))
|
|
);
|
|
},
|
|
moveToStartOfDisplayLine: function(cm) {
|
|
cm.execCommand("goLineLeft");
|
|
return cm.getCursor();
|
|
},
|
|
moveToEndOfDisplayLine: function(cm) {
|
|
cm.execCommand("goLineRight");
|
|
var head = cm.getCursor();
|
|
if (head.sticky == "before") head.ch--;
|
|
return head;
|
|
},
|
|
textObjectManipulation: function(cm, head, motionArgs, vim2) {
|
|
var mirroredPairs = {
|
|
"(": ")",
|
|
")": "(",
|
|
"{": "}",
|
|
"}": "{",
|
|
"[": "]",
|
|
"]": "[",
|
|
"<": ">",
|
|
">": "<"
|
|
};
|
|
var selfPaired = { "'": true, '"': true, "`": true };
|
|
var character = motionArgs.selectedCharacter || "";
|
|
if (character == "b") {
|
|
character = "(";
|
|
} else if (character == "B") {
|
|
character = "{";
|
|
}
|
|
var inclusive = !motionArgs.textObjectInner;
|
|
var tmp, move;
|
|
if (mirroredPairs[character]) {
|
|
move = true;
|
|
tmp = selectCompanionObject(cm, head, character, inclusive);
|
|
if (!tmp) {
|
|
var sc = cm.getSearchCursor(new RegExp("\\" + character, "g"), head);
|
|
if (sc.find()) {
|
|
tmp = selectCompanionObject(cm, sc.from(), character, inclusive);
|
|
}
|
|
}
|
|
} else if (selfPaired[character]) {
|
|
move = true;
|
|
tmp = findBeginningAndEnd(cm, head, character, inclusive);
|
|
} else if (character === "W" || character === "w") {
|
|
var repeat = motionArgs.repeat || 1;
|
|
while (repeat-- > 0) {
|
|
var repeated = expandWordUnderCursor(cm, {
|
|
inclusive,
|
|
innerWord: !inclusive,
|
|
bigWord: character === "W",
|
|
noSymbol: character === "W",
|
|
multiline: true
|
|
}, tmp && tmp.end);
|
|
if (repeated) {
|
|
if (!tmp) tmp = repeated;
|
|
tmp.end = repeated.end;
|
|
}
|
|
}
|
|
} else if (character === "p") {
|
|
tmp = findParagraph(cm, head, motionArgs.repeat, 0, inclusive);
|
|
motionArgs.linewise = true;
|
|
if (vim2.visualMode) {
|
|
if (!vim2.visualLine) {
|
|
vim2.visualLine = true;
|
|
}
|
|
} else {
|
|
var operatorArgs = vim2.inputState.operatorArgs;
|
|
if (operatorArgs) {
|
|
operatorArgs.linewise = true;
|
|
}
|
|
tmp.end.line--;
|
|
}
|
|
} else if (character === "t") {
|
|
tmp = expandTagUnderCursor(cm, head, inclusive);
|
|
} else if (character === "s") {
|
|
var content = cm.getLine(head.line);
|
|
if (head.ch > 0 && isEndOfSentenceSymbol(content[head.ch])) {
|
|
head.ch -= 1;
|
|
}
|
|
var end = getSentence(cm, head, motionArgs.repeat, 1, inclusive);
|
|
var start = getSentence(cm, head, motionArgs.repeat, -1, inclusive);
|
|
if (isWhiteSpaceString(cm.getLine(start.line)[start.ch]) && isWhiteSpaceString(cm.getLine(end.line)[end.ch - 1])) {
|
|
start = { line: start.line, ch: start.ch + 1 };
|
|
}
|
|
tmp = { start, end };
|
|
}
|
|
if (!tmp) {
|
|
return null;
|
|
}
|
|
if (!cm.state.vim.visualMode) {
|
|
return [tmp.start, tmp.end];
|
|
} else {
|
|
return expandSelection(cm, tmp.start, tmp.end, move);
|
|
}
|
|
},
|
|
repeatLastCharacterSearch: function(cm, head, motionArgs) {
|
|
var lastSearch = vimGlobalState.lastCharacterSearch;
|
|
var repeat = motionArgs.repeat;
|
|
var forward = motionArgs.forward === lastSearch.forward;
|
|
var increment = (lastSearch.increment ? 1 : 0) * (forward ? -1 : 1);
|
|
cm.moveH(-increment, "char");
|
|
motionArgs.inclusive = forward ? true : false;
|
|
var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter);
|
|
if (!curEnd) {
|
|
cm.moveH(increment, "char");
|
|
return head;
|
|
}
|
|
curEnd.ch += increment;
|
|
return curEnd;
|
|
}
|
|
};
|
|
function defineMotion(name, fn) {
|
|
motions[name] = fn;
|
|
}
|
|
function fillArray(val, times) {
|
|
var arr = [];
|
|
for (var i = 0; i < times; i++) {
|
|
arr.push(val);
|
|
}
|
|
return arr;
|
|
}
|
|
var operators = {
|
|
change: function(cm, args, ranges) {
|
|
var finalHead, text;
|
|
var vim2 = cm.state.vim;
|
|
var anchor = ranges[0].anchor, head = ranges[0].head;
|
|
if (!vim2.visualMode) {
|
|
text = cm.getRange(anchor, head);
|
|
var lastState = vim2.lastEditInputState;
|
|
if ((lastState == null ? void 0 : lastState.motion) == "moveByWords" && !isWhiteSpaceString(text)) {
|
|
var match = /\s+$/.exec(text);
|
|
if (match && lastState.motionArgs && lastState.motionArgs.forward) {
|
|
head = offsetCursor(head, 0, -match[0].length);
|
|
text = text.slice(0, -match[0].length);
|
|
}
|
|
}
|
|
if (args.linewise) {
|
|
anchor = new Pos2(anchor.line, findFirstNonWhiteSpaceCharacter(cm.getLine(anchor.line)));
|
|
if (head.line > anchor.line) {
|
|
head = new Pos2(head.line - 1, Number.MAX_VALUE);
|
|
}
|
|
}
|
|
cm.replaceRange("", anchor, head);
|
|
finalHead = anchor;
|
|
} else if (args.fullLine) {
|
|
head.ch = Number.MAX_VALUE;
|
|
head.line--;
|
|
cm.setSelection(anchor, head);
|
|
text = cm.getSelection();
|
|
cm.replaceSelection("");
|
|
finalHead = anchor;
|
|
} else {
|
|
text = cm.getSelection();
|
|
var replacement = fillArray("", ranges.length);
|
|
cm.replaceSelections(replacement);
|
|
finalHead = cursorMin(ranges[0].head, ranges[0].anchor);
|
|
}
|
|
vimGlobalState.registerController.pushText(
|
|
args.registerName,
|
|
"change",
|
|
text,
|
|
args.linewise,
|
|
ranges.length > 1
|
|
);
|
|
actions.enterInsertMode(cm, { head: finalHead }, cm.state.vim);
|
|
},
|
|
delete: function(cm, args, ranges) {
|
|
var finalHead, text;
|
|
var vim2 = cm.state.vim;
|
|
if (!vim2.visualBlock) {
|
|
var anchor = ranges[0].anchor, head = ranges[0].head;
|
|
if (args.linewise && head.line != cm.firstLine() && anchor.line == cm.lastLine() && anchor.line == head.line - 1) {
|
|
if (anchor.line == cm.firstLine()) {
|
|
anchor.ch = 0;
|
|
} else {
|
|
anchor = new Pos2(anchor.line - 1, lineLength(cm, anchor.line - 1));
|
|
}
|
|
}
|
|
text = cm.getRange(anchor, head);
|
|
cm.replaceRange("", anchor, head);
|
|
finalHead = anchor;
|
|
if (args.linewise) {
|
|
finalHead = motions.moveToFirstNonWhiteSpaceCharacter(cm, anchor);
|
|
}
|
|
} else {
|
|
text = cm.getSelection();
|
|
var replacement = fillArray("", ranges.length);
|
|
cm.replaceSelections(replacement);
|
|
finalHead = cursorMin(ranges[0].head, ranges[0].anchor);
|
|
}
|
|
vimGlobalState.registerController.pushText(
|
|
args.registerName,
|
|
"delete",
|
|
text,
|
|
args.linewise,
|
|
vim2.visualBlock
|
|
);
|
|
return clipCursorToContent(cm, finalHead);
|
|
},
|
|
indent: function(cm, args, ranges) {
|
|
var vim2 = cm.state.vim;
|
|
var repeat = vim2.visualMode ? args.repeat || 1 : 1;
|
|
if (vim2.visualBlock) {
|
|
var tabSize = cm.getOption("tabSize");
|
|
var indent = cm.getOption("indentWithTabs") ? " " : " ".repeat(tabSize);
|
|
var cursor;
|
|
for (var i = ranges.length - 1; i >= 0; i--) {
|
|
cursor = cursorMin(ranges[i].anchor, ranges[i].head);
|
|
if (args.indentRight) {
|
|
cm.replaceRange(indent.repeat(repeat), cursor, cursor);
|
|
} else {
|
|
var text = cm.getLine(cursor.line);
|
|
var end = 0;
|
|
for (var j = 0; j < repeat; j++) {
|
|
var ch = text[cursor.ch + end];
|
|
if (ch == " ") {
|
|
end++;
|
|
} else if (ch == " ") {
|
|
end++;
|
|
for (var k = 1; k < indent.length; k++) {
|
|
ch = text[cursor.ch + end];
|
|
if (ch !== " ") break;
|
|
end++;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
cm.replaceRange("", cursor, offsetCursor(cursor, 0, end));
|
|
}
|
|
}
|
|
return cursor;
|
|
} else if (cm.indentMore) {
|
|
for (var j = 0; j < repeat; j++) {
|
|
if (args.indentRight) cm.indentMore();
|
|
else cm.indentLess();
|
|
}
|
|
} else {
|
|
var startLine = ranges[0].anchor.line;
|
|
var endLine = vim2.visualBlock ? ranges[ranges.length - 1].anchor.line : ranges[0].head.line;
|
|
if (args.linewise) {
|
|
endLine--;
|
|
}
|
|
for (var i = startLine; i <= endLine; i++) {
|
|
for (var j = 0; j < repeat; j++) {
|
|
cm.indentLine(i, args.indentRight);
|
|
}
|
|
}
|
|
}
|
|
return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);
|
|
},
|
|
indentAuto: function(cm, _args, ranges) {
|
|
cm.execCommand("indentAuto");
|
|
return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);
|
|
},
|
|
hardWrap: function(cm, operatorArgs, ranges, oldAnchor) {
|
|
if (!cm.hardWrap) return;
|
|
var from = ranges[0].anchor.line;
|
|
var to = ranges[0].head.line;
|
|
if (operatorArgs.linewise) to--;
|
|
var endRow = cm.hardWrap({ from, to });
|
|
if (endRow > from && operatorArgs.linewise) endRow--;
|
|
return operatorArgs.keepCursor ? oldAnchor : new Pos2(endRow, 0);
|
|
},
|
|
changeCase: function(cm, args, ranges, oldAnchor, newHead) {
|
|
var selections = cm.getSelections();
|
|
var swapped = [];
|
|
var toLower = args.toLower;
|
|
for (var j = 0; j < selections.length; j++) {
|
|
var toSwap = selections[j];
|
|
var text = "";
|
|
if (toLower === true) {
|
|
text = toSwap.toLowerCase();
|
|
} else if (toLower === false) {
|
|
text = toSwap.toUpperCase();
|
|
} else {
|
|
for (var i = 0; i < toSwap.length; i++) {
|
|
var character = toSwap.charAt(i);
|
|
text += isUpperCase(character) ? character.toLowerCase() : character.toUpperCase();
|
|
}
|
|
}
|
|
swapped.push(text);
|
|
}
|
|
cm.replaceSelections(swapped);
|
|
if (args.shouldMoveCursor) {
|
|
return newHead;
|
|
} else if (!cm.state.vim.visualMode && args.linewise && ranges[0].anchor.line + 1 == ranges[0].head.line) {
|
|
return motions.moveToFirstNonWhiteSpaceCharacter(cm, oldAnchor);
|
|
} else if (args.linewise) {
|
|
return oldAnchor;
|
|
} else {
|
|
return cursorMin(ranges[0].anchor, ranges[0].head);
|
|
}
|
|
},
|
|
yank: function(cm, args, ranges, oldAnchor) {
|
|
var vim2 = cm.state.vim;
|
|
var text = cm.getSelection();
|
|
var endPos = vim2.visualMode ? cursorMin(vim2.sel.anchor, vim2.sel.head, ranges[0].head, ranges[0].anchor) : oldAnchor;
|
|
vimGlobalState.registerController.pushText(
|
|
args.registerName,
|
|
"yank",
|
|
text,
|
|
args.linewise,
|
|
vim2.visualBlock
|
|
);
|
|
return endPos;
|
|
},
|
|
rot13: function(cm, args, ranges, oldAnchor, newHead) {
|
|
var selections = cm.getSelections();
|
|
var swapped = [];
|
|
for (var j = 0; j < selections.length; j++) {
|
|
const replacement = selections[j].split("").map((x) => {
|
|
const code = x.charCodeAt(0);
|
|
if (code >= 65 && code <= 90) {
|
|
return String.fromCharCode(65 + (code - 65 + 13) % 26);
|
|
} else if (code >= 97 && code <= 122) {
|
|
return String.fromCharCode(97 + (code - 97 + 13) % 26);
|
|
} else {
|
|
return x;
|
|
}
|
|
}).join("");
|
|
swapped.push(replacement);
|
|
}
|
|
cm.replaceSelections(swapped);
|
|
if (args.shouldMoveCursor) {
|
|
return newHead;
|
|
} else if (!cm.state.vim.visualMode && args.linewise && ranges[0].anchor.line + 1 == ranges[0].head.line) {
|
|
return motions.moveToFirstNonWhiteSpaceCharacter(cm, oldAnchor);
|
|
} else if (args.linewise) {
|
|
return oldAnchor;
|
|
} else {
|
|
return cursorMin(ranges[0].anchor, ranges[0].head);
|
|
}
|
|
}
|
|
};
|
|
function defineOperator(name, fn) {
|
|
operators[name] = fn;
|
|
}
|
|
var actions = {
|
|
jumpListWalk: function(cm, actionArgs, vim2) {
|
|
if (vim2.visualMode) {
|
|
return;
|
|
}
|
|
var repeat = actionArgs.repeat || 1;
|
|
var forward = actionArgs.forward;
|
|
var jumpList = vimGlobalState.jumpList;
|
|
var mark = jumpList.move(cm, forward ? repeat : -repeat);
|
|
var markPos = mark ? mark.find() : void 0;
|
|
markPos = markPos ? markPos : cm.getCursor();
|
|
cm.setCursor(markPos);
|
|
},
|
|
scroll: function(cm, actionArgs, vim2) {
|
|
if (vim2.visualMode) {
|
|
return;
|
|
}
|
|
var repeat = actionArgs.repeat || 1;
|
|
var lineHeight = cm.defaultTextHeight();
|
|
var top = cm.getScrollInfo().top;
|
|
var delta = lineHeight * repeat;
|
|
var newPos = actionArgs.forward ? top + delta : top - delta;
|
|
var cursor = copyCursor(cm.getCursor());
|
|
var cursorCoords = cm.charCoords(cursor, "local");
|
|
if (actionArgs.forward) {
|
|
if (newPos > cursorCoords.top) {
|
|
cursor.line += (newPos - cursorCoords.top) / lineHeight;
|
|
cursor.line = Math.ceil(cursor.line);
|
|
cm.setCursor(cursor);
|
|
cursorCoords = cm.charCoords(cursor, "local");
|
|
cm.scrollTo(null, cursorCoords.top);
|
|
} else {
|
|
cm.scrollTo(null, newPos);
|
|
}
|
|
} else {
|
|
var newBottom = newPos + cm.getScrollInfo().clientHeight;
|
|
if (newBottom < cursorCoords.bottom) {
|
|
cursor.line -= (cursorCoords.bottom - newBottom) / lineHeight;
|
|
cursor.line = Math.floor(cursor.line);
|
|
cm.setCursor(cursor);
|
|
cursorCoords = cm.charCoords(cursor, "local");
|
|
cm.scrollTo(
|
|
null,
|
|
cursorCoords.bottom - cm.getScrollInfo().clientHeight
|
|
);
|
|
} else {
|
|
cm.scrollTo(null, newPos);
|
|
}
|
|
}
|
|
},
|
|
scrollToCursor: function(cm, actionArgs) {
|
|
var lineNum = cm.getCursor().line;
|
|
var charCoords = cm.charCoords(new Pos2(lineNum, 0), "local");
|
|
var height = cm.getScrollInfo().clientHeight;
|
|
var y = charCoords.top;
|
|
switch (actionArgs.position) {
|
|
case "center":
|
|
y = charCoords.bottom - height / 2;
|
|
break;
|
|
case "bottom":
|
|
var lineLastCharPos = new Pos2(lineNum, cm.getLine(lineNum).length - 1);
|
|
var lineLastCharCoords = cm.charCoords(lineLastCharPos, "local");
|
|
var lineHeight = lineLastCharCoords.bottom - y;
|
|
y = y - height + lineHeight;
|
|
break;
|
|
}
|
|
cm.scrollTo(null, y);
|
|
},
|
|
replayMacro: function(cm, actionArgs, vim2) {
|
|
var registerName = actionArgs.selectedCharacter || "";
|
|
var repeat = actionArgs.repeat || 1;
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
if (registerName == "@") {
|
|
registerName = macroModeState.latestRegister || "";
|
|
} else {
|
|
macroModeState.latestRegister = registerName;
|
|
}
|
|
while (repeat--) {
|
|
executeMacroRegister(cm, vim2, macroModeState, registerName);
|
|
}
|
|
},
|
|
enterMacroRecordMode: function(cm, actionArgs) {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
var registerName = actionArgs.selectedCharacter;
|
|
if (vimGlobalState.registerController.isValidRegister(registerName)) {
|
|
macroModeState.enterMacroRecordMode(cm, registerName);
|
|
}
|
|
},
|
|
toggleOverwrite: function(cm) {
|
|
if (!cm.state.overwrite) {
|
|
cm.toggleOverwrite(true);
|
|
cm.setOption("keyMap", "vim-replace");
|
|
CM.signal(cm, "vim-mode-change", { mode: "replace" });
|
|
} else {
|
|
cm.toggleOverwrite(false);
|
|
cm.setOption("keyMap", "vim-insert");
|
|
CM.signal(cm, "vim-mode-change", { mode: "insert" });
|
|
}
|
|
},
|
|
enterInsertMode: function(cm, actionArgs, vim2) {
|
|
if (cm.getOption("readOnly")) {
|
|
return;
|
|
}
|
|
vim2.insertMode = true;
|
|
vim2.insertModeRepeat = actionArgs && actionArgs.repeat || 1;
|
|
var insertAt = actionArgs ? actionArgs.insertAt : null;
|
|
var sel = vim2.sel;
|
|
var head = actionArgs.head || cm.getCursor("head");
|
|
var height = cm.listSelections().length;
|
|
if (insertAt == "eol") {
|
|
head = new Pos2(head.line, lineLength(cm, head.line));
|
|
} else if (insertAt == "bol") {
|
|
head = new Pos2(head.line, 0);
|
|
} else if (insertAt == "charAfter") {
|
|
var newPosition = updateSelectionForSurrogateCharacters(cm, head, offsetCursor(head, 0, 1));
|
|
head = newPosition.end;
|
|
} else if (insertAt == "firstNonBlank") {
|
|
var newPosition = updateSelectionForSurrogateCharacters(cm, head, motions.moveToFirstNonWhiteSpaceCharacter(cm, head));
|
|
head = newPosition.end;
|
|
} else if (insertAt == "startOfSelectedArea") {
|
|
if (!vim2.visualMode)
|
|
return;
|
|
if (!vim2.visualBlock) {
|
|
if (sel.head.line < sel.anchor.line) {
|
|
head = sel.head;
|
|
} else {
|
|
head = new Pos2(sel.anchor.line, 0);
|
|
}
|
|
} else {
|
|
head = new Pos2(
|
|
Math.min(sel.head.line, sel.anchor.line),
|
|
Math.min(sel.head.ch, sel.anchor.ch)
|
|
);
|
|
height = Math.abs(sel.head.line - sel.anchor.line) + 1;
|
|
}
|
|
} else if (insertAt == "endOfSelectedArea") {
|
|
if (!vim2.visualMode)
|
|
return;
|
|
if (!vim2.visualBlock) {
|
|
if (sel.head.line >= sel.anchor.line) {
|
|
head = offsetCursor(sel.head, 0, 1);
|
|
} else {
|
|
head = new Pos2(sel.anchor.line, 0);
|
|
}
|
|
} else {
|
|
head = new Pos2(
|
|
Math.min(sel.head.line, sel.anchor.line),
|
|
Math.max(sel.head.ch, sel.anchor.ch) + 1
|
|
);
|
|
height = Math.abs(sel.head.line - sel.anchor.line) + 1;
|
|
}
|
|
} else if (insertAt == "inplace") {
|
|
if (vim2.visualMode) {
|
|
return;
|
|
}
|
|
} else if (insertAt == "lastEdit") {
|
|
head = getLastEditPos(cm) || head;
|
|
}
|
|
cm.setOption("disableInput", false);
|
|
if (actionArgs && actionArgs.replace) {
|
|
cm.toggleOverwrite(true);
|
|
cm.setOption("keyMap", "vim-replace");
|
|
CM.signal(cm, "vim-mode-change", { mode: "replace" });
|
|
} else {
|
|
cm.toggleOverwrite(false);
|
|
cm.setOption("keyMap", "vim-insert");
|
|
CM.signal(cm, "vim-mode-change", { mode: "insert" });
|
|
}
|
|
if (!vimGlobalState.macroModeState.isPlaying) {
|
|
cm.on("change", onChange);
|
|
if (vim2.insertEnd) vim2.insertEnd.clear();
|
|
vim2.insertEnd = cm.setBookmark(head, { insertLeft: true });
|
|
CM.on(cm.getInputField(), "keydown", onKeyEventTargetKeyDown);
|
|
}
|
|
if (vim2.visualMode) {
|
|
exitVisualMode(cm);
|
|
}
|
|
selectForInsert(cm, head, height);
|
|
},
|
|
toggleVisualMode: function(cm, actionArgs, vim2) {
|
|
var repeat = actionArgs.repeat;
|
|
var anchor = cm.getCursor();
|
|
var head;
|
|
if (!vim2.visualMode) {
|
|
vim2.visualMode = true;
|
|
vim2.visualLine = !!actionArgs.linewise;
|
|
vim2.visualBlock = !!actionArgs.blockwise;
|
|
head = clipCursorToContent(
|
|
cm,
|
|
new Pos2(anchor.line, anchor.ch + repeat - 1)
|
|
);
|
|
var newPosition = updateSelectionForSurrogateCharacters(cm, anchor, head);
|
|
vim2.sel = {
|
|
anchor: newPosition.start,
|
|
head: newPosition.end
|
|
};
|
|
CM.signal(cm, "vim-mode-change", { mode: "visual", subMode: vim2.visualLine ? "linewise" : vim2.visualBlock ? "blockwise" : "" });
|
|
updateCmSelection(cm);
|
|
updateMark(cm, vim2, "<", cursorMin(anchor, head));
|
|
updateMark(cm, vim2, ">", cursorMax(anchor, head));
|
|
} else if (vim2.visualLine != !!actionArgs.linewise || vim2.visualBlock != !!actionArgs.blockwise) {
|
|
vim2.visualLine = !!actionArgs.linewise;
|
|
vim2.visualBlock = !!actionArgs.blockwise;
|
|
CM.signal(cm, "vim-mode-change", { mode: "visual", subMode: vim2.visualLine ? "linewise" : vim2.visualBlock ? "blockwise" : "" });
|
|
updateCmSelection(cm);
|
|
} else {
|
|
exitVisualMode(cm);
|
|
}
|
|
},
|
|
reselectLastSelection: function(cm, _actionArgs, vim2) {
|
|
var lastSelection = vim2.lastSelection;
|
|
if (vim2.visualMode) {
|
|
updateLastSelection(cm, vim2);
|
|
}
|
|
if (lastSelection) {
|
|
var anchor = lastSelection.anchorMark.find();
|
|
var head = lastSelection.headMark.find();
|
|
if (!anchor || !head) {
|
|
return;
|
|
}
|
|
vim2.sel = {
|
|
anchor,
|
|
head
|
|
};
|
|
vim2.visualMode = true;
|
|
vim2.visualLine = lastSelection.visualLine;
|
|
vim2.visualBlock = lastSelection.visualBlock;
|
|
updateCmSelection(cm);
|
|
updateMark(cm, vim2, "<", cursorMin(anchor, head));
|
|
updateMark(cm, vim2, ">", cursorMax(anchor, head));
|
|
CM.signal(cm, "vim-mode-change", {
|
|
mode: "visual",
|
|
subMode: vim2.visualLine ? "linewise" : vim2.visualBlock ? "blockwise" : ""
|
|
});
|
|
}
|
|
},
|
|
joinLines: function(cm, actionArgs, vim2) {
|
|
var curStart, curEnd;
|
|
if (vim2.visualMode) {
|
|
curStart = cm.getCursor("anchor");
|
|
curEnd = cm.getCursor("head");
|
|
if (cursorIsBefore(curEnd, curStart)) {
|
|
var tmp = curEnd;
|
|
curEnd = curStart;
|
|
curStart = tmp;
|
|
}
|
|
curEnd.ch = lineLength(cm, curEnd.line) - 1;
|
|
} else {
|
|
var repeat = Math.max(actionArgs.repeat, 2);
|
|
curStart = cm.getCursor();
|
|
curEnd = clipCursorToContent(cm, new Pos2(
|
|
curStart.line + repeat - 1,
|
|
Infinity
|
|
));
|
|
}
|
|
var finalCh = 0;
|
|
for (var i = curStart.line; i < curEnd.line; i++) {
|
|
finalCh = lineLength(cm, curStart.line);
|
|
var text = "";
|
|
var nextStartCh = 0;
|
|
if (!actionArgs.keepSpaces) {
|
|
var nextLine = cm.getLine(curStart.line + 1);
|
|
nextStartCh = nextLine.search(/\S/);
|
|
if (nextStartCh == -1) {
|
|
nextStartCh = nextLine.length;
|
|
} else {
|
|
text = " ";
|
|
}
|
|
}
|
|
cm.replaceRange(
|
|
text,
|
|
new Pos2(curStart.line, finalCh),
|
|
new Pos2(curStart.line + 1, nextStartCh)
|
|
);
|
|
}
|
|
var curFinalPos = clipCursorToContent(cm, new Pos2(curStart.line, finalCh));
|
|
if (vim2.visualMode) {
|
|
exitVisualMode(cm, false);
|
|
}
|
|
cm.setCursor(curFinalPos);
|
|
},
|
|
newLineAndEnterInsertMode: function(cm, actionArgs, vim2) {
|
|
vim2.insertMode = true;
|
|
var insertAt = copyCursor(cm.getCursor());
|
|
if (insertAt.line === cm.firstLine() && !actionArgs.after) {
|
|
cm.replaceRange("\n", new Pos2(cm.firstLine(), 0));
|
|
cm.setCursor(cm.firstLine(), 0);
|
|
} else {
|
|
insertAt.line = actionArgs.after ? insertAt.line : insertAt.line - 1;
|
|
insertAt.ch = lineLength(cm, insertAt.line);
|
|
cm.setCursor(insertAt);
|
|
var newlineFn = CM.commands.newlineAndIndentContinueComment || CM.commands.newlineAndIndent;
|
|
newlineFn(cm);
|
|
}
|
|
this.enterInsertMode(cm, { repeat: actionArgs.repeat }, vim2);
|
|
},
|
|
paste: function(cm, actionArgs, vim2) {
|
|
var register = vimGlobalState.registerController.getRegister(
|
|
actionArgs.registerName
|
|
);
|
|
if (actionArgs.registerName === "+") {
|
|
navigator.clipboard.readText().then((value) => {
|
|
this.continuePaste(cm, actionArgs, vim2, value, register);
|
|
});
|
|
} else {
|
|
var text = register.toString();
|
|
this.continuePaste(cm, actionArgs, vim2, text, register);
|
|
}
|
|
},
|
|
continuePaste: function(cm, actionArgs, vim2, text, register) {
|
|
var cur2 = copyCursor(cm.getCursor());
|
|
if (!text) {
|
|
return;
|
|
}
|
|
if (actionArgs.matchIndent) {
|
|
var tabSize = cm.getOption("tabSize");
|
|
var whitespaceLength = function(str) {
|
|
var tabs = str.split(" ").length - 1;
|
|
var spaces = str.split(" ").length - 1;
|
|
return tabs * tabSize + spaces * 1;
|
|
};
|
|
var currentLine = cm.getLine(cm.getCursor().line);
|
|
var indent = whitespaceLength(currentLine.match(/^\s*/)[0]);
|
|
var chompedText = text.replace(/\n$/, "");
|
|
var wasChomped = text !== chompedText;
|
|
var firstIndent = whitespaceLength(text.match(/^\s*/)[0]);
|
|
var text = chompedText.replace(/^\s*/gm, function(wspace) {
|
|
var newIndent = indent + (whitespaceLength(wspace) - firstIndent);
|
|
if (newIndent < 0) {
|
|
return "";
|
|
} else if (cm.getOption("indentWithTabs")) {
|
|
var quotient = Math.floor(newIndent / tabSize);
|
|
return Array(quotient + 1).join(" ");
|
|
} else {
|
|
return Array(newIndent + 1).join(" ");
|
|
}
|
|
});
|
|
text += wasChomped ? "\n" : "";
|
|
}
|
|
if (actionArgs.repeat > 1) {
|
|
text = Array(actionArgs.repeat + 1).join(text);
|
|
}
|
|
var linewise = register.linewise;
|
|
var blockwise = register.blockwise;
|
|
var textLines = blockwise ? text.split("\n") : void 0;
|
|
if (textLines) {
|
|
if (linewise) {
|
|
textLines.pop();
|
|
}
|
|
for (var i = 0; i < textLines.length; i++) {
|
|
textLines[i] = textLines[i] == "" ? " " : textLines[i];
|
|
}
|
|
cur2.ch += actionArgs.after ? 1 : 0;
|
|
cur2.ch = Math.min(lineLength(cm, cur2.line), cur2.ch);
|
|
} else if (linewise) {
|
|
if (vim2.visualMode) {
|
|
text = vim2.visualLine ? text.slice(0, -1) : "\n" + text.slice(0, text.length - 1) + "\n";
|
|
} else if (actionArgs.after) {
|
|
text = "\n" + text.slice(0, text.length - 1);
|
|
cur2.ch = lineLength(cm, cur2.line);
|
|
} else {
|
|
cur2.ch = 0;
|
|
}
|
|
} else {
|
|
cur2.ch += actionArgs.after ? 1 : 0;
|
|
}
|
|
var curPosFinal;
|
|
if (vim2.visualMode) {
|
|
vim2.lastPastedText = text;
|
|
var lastSelectionCurEnd;
|
|
var selectedArea = getSelectedAreaRange(cm);
|
|
var selectionStart = selectedArea[0];
|
|
var selectionEnd = selectedArea[1];
|
|
var selectedText = cm.getSelection();
|
|
var selections = cm.listSelections();
|
|
var emptyStrings = new Array(selections.length).join("1").split("1");
|
|
if (vim2.lastSelection) {
|
|
lastSelectionCurEnd = vim2.lastSelection.headMark.find();
|
|
}
|
|
vimGlobalState.registerController.unnamedRegister.setText(selectedText);
|
|
if (blockwise) {
|
|
cm.replaceSelections(emptyStrings);
|
|
selectionEnd = new Pos2(selectionStart.line + text.length - 1, selectionStart.ch);
|
|
cm.setCursor(selectionStart);
|
|
selectBlock(cm, selectionEnd);
|
|
cm.replaceSelections(text);
|
|
curPosFinal = selectionStart;
|
|
} else if (vim2.visualBlock) {
|
|
cm.replaceSelections(emptyStrings);
|
|
cm.setCursor(selectionStart);
|
|
cm.replaceRange(text, selectionStart, selectionStart);
|
|
curPosFinal = selectionStart;
|
|
} else {
|
|
cm.replaceRange(text, selectionStart, selectionEnd);
|
|
curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1);
|
|
}
|
|
if (lastSelectionCurEnd) {
|
|
vim2.lastSelection.headMark = cm.setBookmark(lastSelectionCurEnd);
|
|
}
|
|
if (linewise) {
|
|
curPosFinal.ch = 0;
|
|
}
|
|
} else {
|
|
if (blockwise && textLines) {
|
|
cm.setCursor(cur2);
|
|
for (var i = 0; i < textLines.length; i++) {
|
|
var line = cur2.line + i;
|
|
if (line > cm.lastLine()) {
|
|
cm.replaceRange("\n", new Pos2(line, 0));
|
|
}
|
|
var lastCh = lineLength(cm, line);
|
|
if (lastCh < cur2.ch) {
|
|
extendLineToColumn(cm, line, cur2.ch);
|
|
}
|
|
}
|
|
cm.setCursor(cur2);
|
|
selectBlock(cm, new Pos2(cur2.line + textLines.length - 1, cur2.ch));
|
|
cm.replaceSelections(textLines);
|
|
curPosFinal = cur2;
|
|
} else {
|
|
cm.replaceRange(text, cur2);
|
|
if (linewise) {
|
|
var line = actionArgs.after ? cur2.line + 1 : cur2.line;
|
|
curPosFinal = new Pos2(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
|
|
} else {
|
|
curPosFinal = copyCursor(cur2);
|
|
if (!/\n/.test(text)) {
|
|
curPosFinal.ch += text.length - (actionArgs.after ? 1 : 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (vim2.visualMode) {
|
|
exitVisualMode(cm, false);
|
|
}
|
|
cm.setCursor(curPosFinal);
|
|
},
|
|
undo: function(cm, actionArgs) {
|
|
cm.operation(function() {
|
|
repeatFn(cm, CM.commands.undo, actionArgs.repeat)();
|
|
cm.setCursor(clipCursorToContent(cm, cm.getCursor("start")));
|
|
});
|
|
},
|
|
redo: function(cm, actionArgs) {
|
|
repeatFn(cm, CM.commands.redo, actionArgs.repeat)();
|
|
},
|
|
setRegister: function(_cm, actionArgs, vim2) {
|
|
vim2.inputState.registerName = actionArgs.selectedCharacter;
|
|
},
|
|
insertRegister: function(cm, actionArgs, vim2) {
|
|
var registerName = actionArgs.selectedCharacter;
|
|
var register = vimGlobalState.registerController.getRegister(registerName);
|
|
var text = register && register.toString();
|
|
if (text) {
|
|
cm.replaceSelection(text);
|
|
}
|
|
},
|
|
oneNormalCommand: function(cm, actionArgs, vim2) {
|
|
exitInsertMode(cm, true);
|
|
vim2.insertModeReturn = true;
|
|
CM.on(cm, "vim-command-done", function handler() {
|
|
if (vim2.visualMode) return;
|
|
if (vim2.insertModeReturn) {
|
|
vim2.insertModeReturn = false;
|
|
if (!vim2.insertMode) {
|
|
actions.enterInsertMode(cm, {}, vim2);
|
|
}
|
|
}
|
|
CM.off(cm, "vim-command-done", handler);
|
|
});
|
|
},
|
|
setMark: function(cm, actionArgs, vim2) {
|
|
var markName = actionArgs.selectedCharacter;
|
|
if (markName) updateMark(cm, vim2, markName, cm.getCursor());
|
|
},
|
|
replace: function(cm, actionArgs, vim2) {
|
|
var replaceWith = actionArgs.selectedCharacter || "";
|
|
var curStart = cm.getCursor();
|
|
var replaceTo;
|
|
var curEnd;
|
|
var selections = cm.listSelections();
|
|
if (vim2.visualMode) {
|
|
curStart = cm.getCursor("start");
|
|
curEnd = cm.getCursor("end");
|
|
} else {
|
|
var line = cm.getLine(curStart.line);
|
|
replaceTo = curStart.ch + actionArgs.repeat;
|
|
if (replaceTo > line.length) {
|
|
replaceTo = line.length;
|
|
}
|
|
curEnd = new Pos2(curStart.line, replaceTo);
|
|
}
|
|
var newPositions = updateSelectionForSurrogateCharacters(cm, curStart, curEnd);
|
|
curStart = newPositions.start;
|
|
curEnd = newPositions.end;
|
|
if (replaceWith == "\n") {
|
|
if (!vim2.visualMode) cm.replaceRange("", curStart, curEnd);
|
|
(CM.commands.newlineAndIndentContinueComment || CM.commands.newlineAndIndent)(cm);
|
|
} else {
|
|
var replaceWithStr = cm.getRange(curStart, curEnd);
|
|
replaceWithStr = replaceWithStr.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, replaceWith);
|
|
replaceWithStr = replaceWithStr.replace(/[^\n]/g, replaceWith);
|
|
if (vim2.visualBlock) {
|
|
var spaces = new Array(cm.getOption("tabSize") + 1).join(" ");
|
|
replaceWithStr = cm.getSelection();
|
|
replaceWithStr = replaceWithStr.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, replaceWith);
|
|
var replaceWithStrings = replaceWithStr.replace(/\t/g, spaces).replace(/[^\n]/g, replaceWith).split("\n");
|
|
cm.replaceSelections(replaceWithStrings);
|
|
} else {
|
|
cm.replaceRange(replaceWithStr, curStart, curEnd);
|
|
}
|
|
if (vim2.visualMode) {
|
|
curStart = cursorIsBefore(selections[0].anchor, selections[0].head) ? selections[0].anchor : selections[0].head;
|
|
cm.setCursor(curStart);
|
|
exitVisualMode(cm, false);
|
|
} else {
|
|
cm.setCursor(offsetCursor(curEnd, 0, -1));
|
|
}
|
|
}
|
|
},
|
|
incrementNumberToken: function(cm, actionArgs) {
|
|
var cur2 = cm.getCursor();
|
|
var lineStr = cm.getLine(cur2.line);
|
|
var re = /(-?)(?:(0x)([\da-f]+)|(0b|0|)(\d+))/gi;
|
|
var match;
|
|
var start;
|
|
var end;
|
|
var numberStr;
|
|
while ((match = re.exec(lineStr)) !== null) {
|
|
start = match.index;
|
|
end = start + match[0].length;
|
|
if (cur2.ch < end) break;
|
|
}
|
|
if (!actionArgs.backtrack && end <= cur2.ch) return;
|
|
if (match) {
|
|
var baseStr = match[2] || match[4];
|
|
var digits = match[3] || match[5];
|
|
var increment = actionArgs.increase ? 1 : -1;
|
|
var base = { "0b": 2, "0": 8, "": 10, "0x": 16 }[baseStr.toLowerCase()];
|
|
var number = parseInt(match[1] + digits, base) + increment * actionArgs.repeat;
|
|
numberStr = number.toString(base);
|
|
var zeroPadding = baseStr ? new Array(digits.length - numberStr.length + 1 + match[1].length).join("0") : "";
|
|
if (numberStr.charAt(0) === "-") {
|
|
numberStr = "-" + baseStr + zeroPadding + numberStr.substr(1);
|
|
} else {
|
|
numberStr = baseStr + zeroPadding + numberStr;
|
|
}
|
|
var from = new Pos2(cur2.line, start);
|
|
var to = new Pos2(cur2.line, end);
|
|
cm.replaceRange(numberStr, from, to);
|
|
} else {
|
|
return;
|
|
}
|
|
cm.setCursor(new Pos2(cur2.line, start + numberStr.length - 1));
|
|
},
|
|
repeatLastEdit: function(cm, actionArgs, vim2) {
|
|
var lastEditInputState = vim2.lastEditInputState;
|
|
if (!lastEditInputState) {
|
|
return;
|
|
}
|
|
var repeat = actionArgs.repeat;
|
|
if (repeat && actionArgs.repeatIsExplicit) {
|
|
lastEditInputState.repeatOverride = repeat;
|
|
} else {
|
|
repeat = lastEditInputState.repeatOverride || repeat;
|
|
}
|
|
repeatLastEdit(
|
|
cm,
|
|
vim2,
|
|
repeat,
|
|
false
|
|
/** repeatForInsert */
|
|
);
|
|
},
|
|
indent: function(cm, actionArgs) {
|
|
cm.indentLine(cm.getCursor().line, actionArgs.indentRight);
|
|
},
|
|
exitInsertMode: function(cm, actionArgs) {
|
|
exitInsertMode(cm);
|
|
}
|
|
};
|
|
function defineAction(name, fn) {
|
|
actions[name] = fn;
|
|
}
|
|
function clipCursorToContent(cm, cur2, oldCur) {
|
|
var vim2 = cm.state.vim;
|
|
var includeLineBreak = vim2.insertMode || vim2.visualMode;
|
|
var line = Math.min(Math.max(cm.firstLine(), cur2.line), cm.lastLine());
|
|
var text = cm.getLine(line);
|
|
var maxCh = text.length - 1 + Number(!!includeLineBreak);
|
|
var ch = Math.min(Math.max(0, cur2.ch), maxCh);
|
|
var charCode = text.charCodeAt(ch);
|
|
if (56320 <= charCode && charCode <= 57343) {
|
|
var direction = 1;
|
|
if (oldCur && oldCur.line == line && oldCur.ch > ch) {
|
|
direction = -1;
|
|
}
|
|
ch += direction;
|
|
if (ch > maxCh) ch -= 2;
|
|
}
|
|
return new Pos2(line, ch);
|
|
}
|
|
function copyArgs(args) {
|
|
var ret = (
|
|
/**@type{typeof args}*/
|
|
{}
|
|
);
|
|
for (var prop in args) {
|
|
if (Object.prototype.hasOwnProperty.call(args, prop)) {
|
|
ret[prop] = args[prop];
|
|
}
|
|
}
|
|
return (
|
|
/**@type{typeof args}*/
|
|
ret
|
|
);
|
|
}
|
|
function offsetCursor(cur2, offsetLine, offsetCh) {
|
|
if (typeof offsetLine === "object") {
|
|
offsetCh = offsetLine.ch;
|
|
offsetLine = offsetLine.line;
|
|
}
|
|
return new Pos2(cur2.line + offsetLine, cur2.ch + offsetCh);
|
|
}
|
|
function commandMatches(keys2, keyMap, context, inputState) {
|
|
if (inputState.operator) context = "operatorPending";
|
|
var match, partial = [], full = [];
|
|
var startIndex = noremap ? keyMap.length - defaultKeymapLength : 0;
|
|
for (var i = startIndex; i < keyMap.length; i++) {
|
|
var command = keyMap[i];
|
|
if (context == "insert" && command.context != "insert" || command.context && command.context != context || inputState.operator && command.type == "action" || !(match = commandMatch(keys2, command.keys))) {
|
|
continue;
|
|
}
|
|
if (match == "partial") {
|
|
partial.push(command);
|
|
}
|
|
if (match == "full") {
|
|
full.push(command);
|
|
}
|
|
}
|
|
return {
|
|
partial,
|
|
full
|
|
};
|
|
}
|
|
function commandMatch(pressed, mapped) {
|
|
const isLastCharacter = mapped.slice(-11) == "<character>";
|
|
const isLastRegister = mapped.slice(-10) == "<register>";
|
|
if (isLastCharacter || isLastRegister) {
|
|
var prefixLen = mapped.length - (isLastCharacter ? 11 : 10);
|
|
var pressedPrefix = pressed.slice(0, prefixLen);
|
|
var mappedPrefix = mapped.slice(0, prefixLen);
|
|
return pressedPrefix == mappedPrefix && pressed.length > prefixLen ? "full" : mappedPrefix.indexOf(pressedPrefix) == 0 ? "partial" : false;
|
|
} else {
|
|
return pressed == mapped ? "full" : mapped.indexOf(pressed) == 0 ? "partial" : false;
|
|
}
|
|
}
|
|
function lastChar(keys2) {
|
|
var match = /^.*(<[^>]+>)$/.exec(keys2);
|
|
var selectedCharacter = match ? match[1] : keys2.slice(-1);
|
|
if (selectedCharacter.length > 1) {
|
|
switch (selectedCharacter) {
|
|
case "<CR>":
|
|
case "<S-CR>":
|
|
selectedCharacter = "\n";
|
|
break;
|
|
case "<Space>":
|
|
case "<S-Space>":
|
|
selectedCharacter = " ";
|
|
break;
|
|
default:
|
|
selectedCharacter = "";
|
|
break;
|
|
}
|
|
}
|
|
return selectedCharacter;
|
|
}
|
|
function repeatFn(cm, fn, repeat) {
|
|
return function() {
|
|
for (var i = 0; i < repeat; i++) {
|
|
fn(cm);
|
|
}
|
|
};
|
|
}
|
|
function copyCursor(cur2) {
|
|
return new Pos2(cur2.line, cur2.ch);
|
|
}
|
|
function cursorEqual(cur1, cur2) {
|
|
return cur1.ch == cur2.ch && cur1.line == cur2.line;
|
|
}
|
|
function cursorIsBefore(cur1, cur2) {
|
|
if (cur1.line < cur2.line) {
|
|
return true;
|
|
}
|
|
if (cur1.line == cur2.line && cur1.ch < cur2.ch) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
function cursorMin(cur1, cur2) {
|
|
if (arguments.length > 2) {
|
|
cur2 = cursorMin.apply(void 0, Array.prototype.slice.call(arguments, 1));
|
|
}
|
|
return cursorIsBefore(cur1, cur2) ? cur1 : cur2;
|
|
}
|
|
function cursorMax(cur1, cur2) {
|
|
if (arguments.length > 2) {
|
|
cur2 = cursorMax.apply(void 0, Array.prototype.slice.call(arguments, 1));
|
|
}
|
|
return cursorIsBefore(cur1, cur2) ? cur2 : cur1;
|
|
}
|
|
function cursorIsBetween(cur1, cur2, cur3) {
|
|
var cur1before2 = cursorIsBefore(cur1, cur2);
|
|
var cur2before3 = cursorIsBefore(cur2, cur3);
|
|
return cur1before2 && cur2before3;
|
|
}
|
|
function lineLength(cm, lineNum) {
|
|
return cm.getLine(lineNum).length;
|
|
}
|
|
function trim(s) {
|
|
if (s.trim) {
|
|
return s.trim();
|
|
}
|
|
return s.replace(/^\s+|\s+$/g, "");
|
|
}
|
|
function escapeRegex(s) {
|
|
return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, "\\$1");
|
|
}
|
|
function extendLineToColumn(cm, lineNum, column) {
|
|
var endCh = lineLength(cm, lineNum);
|
|
var spaces = new Array(column - endCh + 1).join(" ");
|
|
cm.setCursor(new Pos2(lineNum, endCh));
|
|
cm.replaceRange(spaces, cm.getCursor());
|
|
}
|
|
function selectBlock(cm, selectionEnd) {
|
|
var selections = [], ranges = cm.listSelections();
|
|
var head = copyCursor(cm.clipPos(selectionEnd));
|
|
var isClipped = !cursorEqual(selectionEnd, head);
|
|
var curHead = cm.getCursor("head");
|
|
var primIndex = getIndex(ranges, curHead);
|
|
var wasClipped = cursorEqual(ranges[primIndex].head, ranges[primIndex].anchor);
|
|
var max = ranges.length - 1;
|
|
var index = max - primIndex > primIndex ? max : 0;
|
|
var base = ranges[index].anchor;
|
|
var firstLine = Math.min(base.line, head.line);
|
|
var lastLine = Math.max(base.line, head.line);
|
|
var baseCh = base.ch, headCh = head.ch;
|
|
var dir = ranges[index].head.ch - baseCh;
|
|
var newDir = headCh - baseCh;
|
|
if (dir > 0 && newDir <= 0) {
|
|
baseCh++;
|
|
if (!isClipped) {
|
|
headCh--;
|
|
}
|
|
} else if (dir < 0 && newDir >= 0) {
|
|
baseCh--;
|
|
if (!wasClipped) {
|
|
headCh++;
|
|
}
|
|
} else if (dir < 0 && newDir == -1) {
|
|
baseCh--;
|
|
headCh++;
|
|
}
|
|
for (var line = firstLine; line <= lastLine; line++) {
|
|
var range = { anchor: new Pos2(line, baseCh), head: new Pos2(line, headCh) };
|
|
selections.push(range);
|
|
}
|
|
cm.setSelections(selections);
|
|
selectionEnd.ch = headCh;
|
|
base.ch = baseCh;
|
|
return base;
|
|
}
|
|
function selectForInsert(cm, head, height) {
|
|
var sel = [];
|
|
for (var i = 0; i < height; i++) {
|
|
var lineHead = offsetCursor(head, i, 0);
|
|
sel.push({ anchor: lineHead, head: lineHead });
|
|
}
|
|
cm.setSelections(sel, 0);
|
|
}
|
|
function getIndex(ranges, cursor, end) {
|
|
for (var i = 0; i < ranges.length; i++) {
|
|
var atAnchor = cursorEqual(ranges[i].anchor, cursor);
|
|
var atHead = cursorEqual(ranges[i].head, cursor);
|
|
if (atAnchor || atHead) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
function getSelectedAreaRange(cm, vim2) {
|
|
var selections = cm.listSelections();
|
|
var start = selections[0];
|
|
var end = selections[selections.length - 1];
|
|
var selectionStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;
|
|
var selectionEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
|
|
return [selectionStart, selectionEnd];
|
|
}
|
|
function updateLastSelection(cm, vim2) {
|
|
var anchor = vim2.sel.anchor;
|
|
var head = vim2.sel.head;
|
|
if (vim2.lastPastedText) {
|
|
head = cm.posFromIndex(cm.indexFromPos(anchor) + vim2.lastPastedText.length);
|
|
vim2.lastPastedText = void 0;
|
|
}
|
|
vim2.lastSelection = {
|
|
"anchorMark": cm.setBookmark(anchor),
|
|
"headMark": cm.setBookmark(head),
|
|
"anchor": copyCursor(anchor),
|
|
"head": copyCursor(head),
|
|
"visualMode": vim2.visualMode,
|
|
"visualLine": vim2.visualLine,
|
|
"visualBlock": vim2.visualBlock
|
|
};
|
|
}
|
|
function expandSelection(cm, start, end, move) {
|
|
var sel = cm.state.vim.sel;
|
|
var head = move ? start : sel.head;
|
|
var anchor = move ? start : sel.anchor;
|
|
var tmp;
|
|
if (cursorIsBefore(end, start)) {
|
|
tmp = end;
|
|
end = start;
|
|
start = tmp;
|
|
}
|
|
if (cursorIsBefore(head, anchor)) {
|
|
head = cursorMin(start, head);
|
|
anchor = cursorMax(anchor, end);
|
|
} else {
|
|
anchor = cursorMin(start, anchor);
|
|
head = cursorMax(head, end);
|
|
head = offsetCursor(head, 0, -1);
|
|
if (head.ch == -1 && head.line != cm.firstLine()) {
|
|
head = new Pos2(head.line - 1, lineLength(cm, head.line - 1));
|
|
}
|
|
}
|
|
return [anchor, head];
|
|
}
|
|
function updateCmSelection(cm, sel, mode) {
|
|
var vim2 = cm.state.vim;
|
|
sel = sel || vim2.sel;
|
|
if (!mode) {
|
|
mode = vim2.visualLine ? "line" : vim2.visualBlock ? "block" : "char";
|
|
}
|
|
var cmSel = makeCmSelection(cm, sel, mode);
|
|
cm.setSelections(cmSel.ranges, cmSel.primary);
|
|
}
|
|
function makeCmSelection(cm, sel, mode, exclusive) {
|
|
var head = copyCursor(sel.head);
|
|
var anchor = copyCursor(sel.anchor);
|
|
if (mode == "char") {
|
|
var headOffset = !exclusive && !cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
|
|
var anchorOffset = cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
|
|
head = offsetCursor(sel.head, 0, headOffset);
|
|
anchor = offsetCursor(sel.anchor, 0, anchorOffset);
|
|
return {
|
|
ranges: [{ anchor, head }],
|
|
primary: 0
|
|
};
|
|
} else if (mode == "line") {
|
|
if (!cursorIsBefore(sel.head, sel.anchor)) {
|
|
anchor.ch = 0;
|
|
var lastLine = cm.lastLine();
|
|
if (head.line > lastLine) {
|
|
head.line = lastLine;
|
|
}
|
|
head.ch = lineLength(cm, head.line);
|
|
} else {
|
|
head.ch = 0;
|
|
anchor.ch = lineLength(cm, anchor.line);
|
|
}
|
|
return {
|
|
ranges: [{ anchor, head }],
|
|
primary: 0
|
|
};
|
|
} else if (mode == "block") {
|
|
var top = Math.min(anchor.line, head.line), fromCh = anchor.ch, bottom = Math.max(anchor.line, head.line), toCh = head.ch;
|
|
if (fromCh < toCh) {
|
|
toCh += 1;
|
|
} else {
|
|
fromCh += 1;
|
|
}
|
|
var height = bottom - top + 1;
|
|
var primary = head.line == top ? 0 : height - 1;
|
|
var ranges = [];
|
|
for (var i = 0; i < height; i++) {
|
|
ranges.push({
|
|
anchor: new Pos2(top + i, fromCh),
|
|
head: new Pos2(top + i, toCh)
|
|
});
|
|
}
|
|
return {
|
|
ranges,
|
|
primary
|
|
};
|
|
}
|
|
throw "never happens";
|
|
}
|
|
function getHead(cm) {
|
|
var cur2 = cm.getCursor("head");
|
|
if (cm.getSelection().length == 1) {
|
|
cur2 = cursorMin(cur2, cm.getCursor("anchor"));
|
|
}
|
|
return cur2;
|
|
}
|
|
function exitVisualMode(cm, moveHead) {
|
|
var vim2 = cm.state.vim;
|
|
if (moveHead !== false) {
|
|
cm.setCursor(clipCursorToContent(cm, vim2.sel.head));
|
|
}
|
|
updateLastSelection(cm, vim2);
|
|
vim2.visualMode = false;
|
|
vim2.visualLine = false;
|
|
vim2.visualBlock = false;
|
|
if (!vim2.insertMode) CM.signal(cm, "vim-mode-change", { mode: "normal" });
|
|
}
|
|
function clipToLine(cm, curStart, curEnd) {
|
|
var selection = cm.getRange(curStart, curEnd);
|
|
if (/\n\s*$/.test(selection)) {
|
|
var lines = selection.split("\n");
|
|
lines.pop();
|
|
for (var line = lines.pop(); lines.length > 0 && line && isWhiteSpaceString(line); line = lines.pop()) {
|
|
curEnd.line--;
|
|
curEnd.ch = 0;
|
|
}
|
|
if (line) {
|
|
curEnd.line--;
|
|
curEnd.ch = lineLength(cm, curEnd.line);
|
|
} else {
|
|
curEnd.ch = 0;
|
|
}
|
|
}
|
|
}
|
|
function expandSelectionToLine(_cm, curStart, curEnd) {
|
|
curStart.ch = 0;
|
|
curEnd.ch = 0;
|
|
curEnd.line++;
|
|
}
|
|
function findFirstNonWhiteSpaceCharacter(text) {
|
|
if (!text) {
|
|
return 0;
|
|
}
|
|
var firstNonWS = text.search(/\S/);
|
|
return firstNonWS == -1 ? text.length : firstNonWS;
|
|
}
|
|
function expandWordUnderCursor(cm, { inclusive, innerWord, bigWord, noSymbol, multiline }, cursor) {
|
|
var cur2 = cursor || getHead(cm);
|
|
var line = cm.getLine(cur2.line);
|
|
var endLine = line;
|
|
var startLineNumber = cur2.line;
|
|
var endLineNumber = startLineNumber;
|
|
var idx = cur2.ch;
|
|
var wordOnNextLine;
|
|
var test = noSymbol ? wordCharTest[0] : bigWordCharTest[0];
|
|
if (innerWord && /\s/.test(line.charAt(idx))) {
|
|
test = function(ch) {
|
|
return /\s/.test(ch);
|
|
};
|
|
} else {
|
|
while (!test(line.charAt(idx))) {
|
|
idx++;
|
|
if (idx >= line.length) {
|
|
if (!multiline) return null;
|
|
idx--;
|
|
wordOnNextLine = findWord(cm, cur2, true, bigWord, true);
|
|
break;
|
|
}
|
|
}
|
|
if (bigWord) {
|
|
test = bigWordCharTest[0];
|
|
} else {
|
|
test = wordCharTest[0];
|
|
if (!test(line.charAt(idx))) {
|
|
test = wordCharTest[1];
|
|
}
|
|
}
|
|
}
|
|
var end = idx, start = idx;
|
|
while (test(line.charAt(start)) && start >= 0) {
|
|
start--;
|
|
}
|
|
start++;
|
|
if (wordOnNextLine) {
|
|
end = wordOnNextLine.to;
|
|
endLineNumber = wordOnNextLine.line;
|
|
endLine = cm.getLine(endLineNumber);
|
|
if (!endLine && end == 0) end++;
|
|
} else {
|
|
while (test(line.charAt(end)) && end < line.length) {
|
|
end++;
|
|
}
|
|
}
|
|
if (inclusive) {
|
|
var wordEnd = end;
|
|
var startsWithSpace = cur2.ch <= start && /\s/.test(line.charAt(cur2.ch));
|
|
if (!startsWithSpace) {
|
|
while (/\s/.test(endLine.charAt(end)) && end < endLine.length) {
|
|
end++;
|
|
}
|
|
}
|
|
if (wordEnd == end || startsWithSpace) {
|
|
var wordStart = start;
|
|
while (/\s/.test(line.charAt(start - 1)) && start > 0) {
|
|
start--;
|
|
}
|
|
if (!start && !startsWithSpace) {
|
|
start = wordStart;
|
|
}
|
|
}
|
|
}
|
|
return { start: new Pos2(startLineNumber, start), end: new Pos2(endLineNumber, end) };
|
|
}
|
|
function expandTagUnderCursor(cm, head, inclusive) {
|
|
var cur2 = head;
|
|
if (!CM.findMatchingTag || !CM.findEnclosingTag) {
|
|
return { start: cur2, end: cur2 };
|
|
}
|
|
var tags3 = CM.findMatchingTag(cm, head) || CM.findEnclosingTag(cm, head);
|
|
if (!tags3 || !tags3.open || !tags3.close) {
|
|
return { start: cur2, end: cur2 };
|
|
}
|
|
if (inclusive) {
|
|
return { start: tags3.open.from, end: tags3.close.to };
|
|
}
|
|
return { start: tags3.open.to, end: tags3.close.from };
|
|
}
|
|
function recordJumpPosition(cm, oldCur, newCur) {
|
|
if (!cursorEqual(oldCur, newCur)) {
|
|
vimGlobalState.jumpList.add(cm, oldCur, newCur);
|
|
}
|
|
}
|
|
function recordLastCharacterSearch(increment, args) {
|
|
vimGlobalState.lastCharacterSearch.increment = increment;
|
|
vimGlobalState.lastCharacterSearch.forward = args.forward;
|
|
vimGlobalState.lastCharacterSearch.selectedCharacter = args.selectedCharacter;
|
|
}
|
|
var symbolToMode = {
|
|
"(": "bracket",
|
|
")": "bracket",
|
|
"{": "bracket",
|
|
"}": "bracket",
|
|
"[": "section",
|
|
"]": "section",
|
|
"*": "comment",
|
|
"/": "comment",
|
|
"m": "method",
|
|
"M": "method",
|
|
"#": "preprocess"
|
|
};
|
|
var findSymbolModes = {
|
|
bracket: {
|
|
isComplete: function(state) {
|
|
if (state.nextCh === state.symb) {
|
|
state.depth++;
|
|
if (state.depth >= 1) return true;
|
|
} else if (state.nextCh === state.reverseSymb) {
|
|
state.depth--;
|
|
}
|
|
return false;
|
|
}
|
|
},
|
|
section: {
|
|
init: function(state) {
|
|
state.curMoveThrough = true;
|
|
state.symb = (state.forward ? "]" : "[") === state.symb ? "{" : "}";
|
|
},
|
|
isComplete: function(state) {
|
|
return state.index === 0 && state.nextCh === state.symb;
|
|
}
|
|
},
|
|
comment: {
|
|
isComplete: function(state) {
|
|
var found = state.lastCh === "*" && state.nextCh === "/";
|
|
state.lastCh = state.nextCh;
|
|
return found;
|
|
}
|
|
},
|
|
// TODO: The original Vim implementation only operates on level 1 and 2.
|
|
// The current implementation doesn't check for code block level and
|
|
// therefore it operates on any levels.
|
|
method: {
|
|
init: function(state) {
|
|
state.symb = state.symb === "m" ? "{" : "}";
|
|
state.reverseSymb = state.symb === "{" ? "}" : "{";
|
|
},
|
|
isComplete: function(state) {
|
|
if (state.nextCh === state.symb) return true;
|
|
return false;
|
|
}
|
|
},
|
|
preprocess: {
|
|
init: function(state) {
|
|
state.index = 0;
|
|
},
|
|
isComplete: function(state) {
|
|
var _a;
|
|
if (state.nextCh === "#") {
|
|
var token = (_a = state.lineText.match(/^#(\w+)/)) == null ? void 0 : _a[1];
|
|
if (token === "endif") {
|
|
if (state.forward && state.depth === 0) {
|
|
return true;
|
|
}
|
|
state.depth++;
|
|
} else if (token === "if") {
|
|
if (!state.forward && state.depth === 0) {
|
|
return true;
|
|
}
|
|
state.depth--;
|
|
}
|
|
if (token === "else" && state.depth === 0) return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
function findSymbol(cm, repeat, forward, symb) {
|
|
var cur2 = copyCursor(cm.getCursor());
|
|
var increment = forward ? 1 : -1;
|
|
var endLine = forward ? cm.lineCount() : -1;
|
|
var curCh = cur2.ch;
|
|
var line = cur2.line;
|
|
var lineText = cm.getLine(line);
|
|
var state = {
|
|
lineText,
|
|
nextCh: lineText.charAt(curCh),
|
|
lastCh: null,
|
|
index: curCh,
|
|
symb,
|
|
reverseSymb: (forward ? { ")": "(", "}": "{" } : { "(": ")", "{": "}" })[symb],
|
|
forward,
|
|
depth: 0,
|
|
curMoveThrough: false
|
|
};
|
|
var mode = symbolToMode[symb];
|
|
if (!mode) return cur2;
|
|
var init = findSymbolModes[mode].init;
|
|
var isComplete = findSymbolModes[mode].isComplete;
|
|
if (init) {
|
|
init(state);
|
|
}
|
|
while (line !== endLine && repeat) {
|
|
state.index += increment;
|
|
state.nextCh = state.lineText.charAt(state.index);
|
|
if (!state.nextCh) {
|
|
line += increment;
|
|
state.lineText = cm.getLine(line) || "";
|
|
if (increment > 0) {
|
|
state.index = 0;
|
|
} else {
|
|
var lineLen = state.lineText.length;
|
|
state.index = lineLen > 0 ? lineLen - 1 : 0;
|
|
}
|
|
state.nextCh = state.lineText.charAt(state.index);
|
|
}
|
|
if (isComplete(state)) {
|
|
cur2.line = line;
|
|
cur2.ch = state.index;
|
|
repeat--;
|
|
}
|
|
}
|
|
if (state.nextCh || state.curMoveThrough) {
|
|
return new Pos2(line, state.index);
|
|
}
|
|
return cur2;
|
|
}
|
|
function findWord(cm, cur2, forward, bigWord, emptyLineIsWord) {
|
|
var lineNum = cur2.line;
|
|
var pos = cur2.ch;
|
|
var line = cm.getLine(lineNum);
|
|
var dir = forward ? 1 : -1;
|
|
var charTests = bigWord ? bigWordCharTest : wordCharTest;
|
|
if (emptyLineIsWord && line == "") {
|
|
lineNum += dir;
|
|
line = cm.getLine(lineNum);
|
|
if (!isLine(cm, lineNum)) {
|
|
return null;
|
|
}
|
|
pos = forward ? 0 : line.length;
|
|
}
|
|
while (true) {
|
|
if (emptyLineIsWord && line == "") {
|
|
return { from: 0, to: 0, line: lineNum };
|
|
}
|
|
var stop = dir > 0 ? line.length : -1;
|
|
var wordStart = stop, wordEnd = stop;
|
|
while (pos != stop) {
|
|
var foundWord = false;
|
|
for (var i = 0; i < charTests.length && !foundWord; ++i) {
|
|
if (charTests[i](line.charAt(pos))) {
|
|
wordStart = pos;
|
|
while (pos != stop && charTests[i](line.charAt(pos))) {
|
|
pos += dir;
|
|
}
|
|
wordEnd = pos;
|
|
foundWord = wordStart != wordEnd;
|
|
if (wordStart == cur2.ch && lineNum == cur2.line && wordEnd == wordStart + dir) {
|
|
continue;
|
|
} else {
|
|
return {
|
|
from: Math.min(wordStart, wordEnd + 1),
|
|
to: Math.max(wordStart, wordEnd),
|
|
line: lineNum
|
|
};
|
|
}
|
|
}
|
|
}
|
|
if (!foundWord) {
|
|
pos += dir;
|
|
}
|
|
}
|
|
lineNum += dir;
|
|
if (!isLine(cm, lineNum)) {
|
|
return null;
|
|
}
|
|
line = cm.getLine(lineNum);
|
|
pos = dir > 0 ? 0 : line.length;
|
|
}
|
|
}
|
|
function moveToWord(cm, cur2, repeat, forward, wordEnd, bigWord) {
|
|
var curStart = copyCursor(cur2);
|
|
var words = [];
|
|
if (forward && !wordEnd || !forward && wordEnd) {
|
|
repeat++;
|
|
}
|
|
var emptyLineIsWord = !(forward && wordEnd);
|
|
for (var i = 0; i < repeat; i++) {
|
|
var word = findWord(cm, cur2, forward, bigWord, emptyLineIsWord);
|
|
if (!word) {
|
|
var eodCh = lineLength(cm, cm.lastLine());
|
|
words.push(forward ? { line: cm.lastLine(), from: eodCh, to: eodCh } : { line: 0, from: 0, to: 0 });
|
|
break;
|
|
}
|
|
words.push(word);
|
|
cur2 = new Pos2(word.line, forward ? word.to - 1 : word.from);
|
|
}
|
|
var shortCircuit = words.length != repeat;
|
|
var firstWord = words[0];
|
|
var lastWord = words.pop();
|
|
if (forward && !wordEnd) {
|
|
if (!shortCircuit && (firstWord.from != curStart.ch || firstWord.line != curStart.line)) {
|
|
lastWord = words.pop();
|
|
}
|
|
return lastWord && new Pos2(lastWord.line, lastWord.from);
|
|
} else if (forward && wordEnd) {
|
|
return lastWord && new Pos2(lastWord.line, lastWord.to - 1);
|
|
} else if (!forward && wordEnd) {
|
|
if (!shortCircuit && (firstWord.to != curStart.ch || firstWord.line != curStart.line)) {
|
|
lastWord = words.pop();
|
|
}
|
|
return lastWord && new Pos2(lastWord.line, lastWord.to);
|
|
} else {
|
|
return lastWord && new Pos2(lastWord.line, lastWord.from);
|
|
}
|
|
}
|
|
function moveToEol(cm, head, motionArgs, vim2, keepHPos) {
|
|
var cur2 = head;
|
|
var retval = new Pos2(cur2.line + motionArgs.repeat - 1, Infinity);
|
|
var end = cm.clipPos(retval);
|
|
end.ch--;
|
|
if (!keepHPos) {
|
|
vim2.lastHPos = Infinity;
|
|
vim2.lastHSPos = cm.charCoords(end, "div").left;
|
|
}
|
|
return retval;
|
|
}
|
|
function moveToCharacter(cm, repeat, forward, character, head) {
|
|
if (!character) return;
|
|
var cur2 = head || cm.getCursor();
|
|
var start = cur2.ch;
|
|
var idx;
|
|
for (var i = 0; i < repeat; i++) {
|
|
var line = cm.getLine(cur2.line);
|
|
idx = charIdxInLine(start, line, character, forward);
|
|
if (idx == -1) {
|
|
return void 0;
|
|
}
|
|
start = idx;
|
|
}
|
|
if (idx != void 0)
|
|
return new Pos2(cm.getCursor().line, idx);
|
|
}
|
|
function moveToColumn(cm, repeat) {
|
|
var line = cm.getCursor().line;
|
|
return clipCursorToContent(cm, new Pos2(line, repeat - 1));
|
|
}
|
|
function updateMark(cm, vim2, markName, pos) {
|
|
if (!inArray(markName, validMarks) && !latinCharRegex.test(markName)) {
|
|
return;
|
|
}
|
|
if (vim2.marks[markName]) {
|
|
vim2.marks[markName].clear();
|
|
}
|
|
vim2.marks[markName] = cm.setBookmark(pos);
|
|
}
|
|
function charIdxInLine(start, line, character, forward, includeChar) {
|
|
var idx;
|
|
if (forward) {
|
|
idx = line.indexOf(character, start + 1);
|
|
} else {
|
|
idx = line.lastIndexOf(character, start - 1);
|
|
}
|
|
return idx;
|
|
}
|
|
function findParagraph(cm, head, repeat, dir, inclusive) {
|
|
var line = head.line;
|
|
var min = cm.firstLine();
|
|
var max = cm.lastLine();
|
|
var start, end, i = line;
|
|
function isEmpty(i2) {
|
|
return !cm.getLine(i2);
|
|
}
|
|
function isBoundary(i2, dir2, any) {
|
|
if (any) {
|
|
return isEmpty(i2) != isEmpty(i2 + dir2);
|
|
}
|
|
return !isEmpty(i2) && isEmpty(i2 + dir2);
|
|
}
|
|
if (dir) {
|
|
while (min <= i && i <= max && repeat > 0) {
|
|
if (isBoundary(i, dir)) {
|
|
repeat--;
|
|
}
|
|
i += dir;
|
|
}
|
|
return { start: new Pos2(i, 0), end: head };
|
|
}
|
|
var vim2 = cm.state.vim;
|
|
if (vim2.visualLine && isBoundary(line, 1, true)) {
|
|
var anchor = vim2.sel.anchor;
|
|
if (isBoundary(anchor.line, -1, true)) {
|
|
if (!inclusive || anchor.line != line) {
|
|
line += 1;
|
|
}
|
|
}
|
|
}
|
|
var startState = isEmpty(line);
|
|
for (i = line; i <= max && repeat; i++) {
|
|
if (isBoundary(i, 1, true)) {
|
|
if (!inclusive || isEmpty(i) != startState) {
|
|
repeat--;
|
|
}
|
|
}
|
|
}
|
|
end = new Pos2(i, 0);
|
|
if (i > max && !startState) {
|
|
startState = true;
|
|
} else {
|
|
inclusive = false;
|
|
}
|
|
for (i = line; i > min; i--) {
|
|
if (!inclusive || isEmpty(i) == startState || i == line) {
|
|
if (isBoundary(i, -1, true)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
start = new Pos2(i, 0);
|
|
return { start, end };
|
|
}
|
|
function getSentence(cm, cur2, repeat, dir, inclusive) {
|
|
function nextChar2(curr) {
|
|
if (curr.line === null) return;
|
|
if (curr.pos + curr.dir < 0 || curr.pos + curr.dir >= curr.line.length) {
|
|
curr.line = null;
|
|
} else {
|
|
curr.pos += curr.dir;
|
|
}
|
|
}
|
|
function forward(cm2, ln, pos, dir2) {
|
|
var line = cm2.getLine(ln);
|
|
var curr = {
|
|
line,
|
|
ln,
|
|
pos,
|
|
dir: dir2
|
|
};
|
|
if (curr.line === "") {
|
|
return { ln: curr.ln, pos: curr.pos };
|
|
}
|
|
var lastSentencePos = curr.pos;
|
|
nextChar2(curr);
|
|
while (curr.line !== null) {
|
|
lastSentencePos = curr.pos;
|
|
if (isEndOfSentenceSymbol(curr.line[curr.pos])) {
|
|
if (!inclusive) {
|
|
return { ln: curr.ln, pos: curr.pos + 1 };
|
|
} else {
|
|
nextChar2(curr);
|
|
while (curr.line !== null) {
|
|
if (isWhiteSpaceString(curr.line[curr.pos])) {
|
|
lastSentencePos = curr.pos;
|
|
nextChar2(curr);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return { ln: curr.ln, pos: lastSentencePos + 1 };
|
|
}
|
|
}
|
|
nextChar2(curr);
|
|
}
|
|
return { ln: curr.ln, pos: lastSentencePos + 1 };
|
|
}
|
|
function reverse(cm2, ln, pos, dir2) {
|
|
var line = cm2.getLine(ln);
|
|
var curr = {
|
|
line,
|
|
ln,
|
|
pos,
|
|
dir: dir2
|
|
};
|
|
if (curr.line === "") {
|
|
return { ln: curr.ln, pos: curr.pos };
|
|
}
|
|
var lastSentencePos = curr.pos;
|
|
nextChar2(curr);
|
|
while (curr.line !== null) {
|
|
if (!isWhiteSpaceString(curr.line[curr.pos]) && !isEndOfSentenceSymbol(curr.line[curr.pos])) {
|
|
lastSentencePos = curr.pos;
|
|
} else if (isEndOfSentenceSymbol(curr.line[curr.pos])) {
|
|
if (!inclusive) {
|
|
return { ln: curr.ln, pos: lastSentencePos };
|
|
} else {
|
|
if (isWhiteSpaceString(curr.line[curr.pos + 1])) {
|
|
return { ln: curr.ln, pos: curr.pos + 1 };
|
|
} else {
|
|
return { ln: curr.ln, pos: lastSentencePos };
|
|
}
|
|
}
|
|
}
|
|
nextChar2(curr);
|
|
}
|
|
curr.line = line;
|
|
if (inclusive && isWhiteSpaceString(curr.line[curr.pos])) {
|
|
return { ln: curr.ln, pos: curr.pos };
|
|
} else {
|
|
return { ln: curr.ln, pos: lastSentencePos };
|
|
}
|
|
}
|
|
var curr_index = {
|
|
ln: cur2.line,
|
|
pos: cur2.ch
|
|
};
|
|
while (repeat > 0) {
|
|
if (dir < 0) {
|
|
curr_index = reverse(cm, curr_index.ln, curr_index.pos, dir);
|
|
} else {
|
|
curr_index = forward(cm, curr_index.ln, curr_index.pos, dir);
|
|
}
|
|
repeat--;
|
|
}
|
|
return new Pos2(curr_index.ln, curr_index.pos);
|
|
}
|
|
function findSentence(cm, cur2, repeat, dir) {
|
|
function nextChar2(cm2, idx) {
|
|
if (idx.line === null) return;
|
|
if (idx.pos + idx.dir < 0 || idx.pos + idx.dir >= idx.line.length) {
|
|
idx.ln += idx.dir;
|
|
if (!isLine(cm2, idx.ln)) {
|
|
idx.line = null;
|
|
return;
|
|
}
|
|
idx.line = cm2.getLine(idx.ln);
|
|
idx.pos = idx.dir > 0 ? 0 : idx.line.length - 1;
|
|
} else {
|
|
idx.pos += idx.dir;
|
|
}
|
|
}
|
|
function forward(cm2, ln, pos, dir2) {
|
|
var line = cm2.getLine(ln);
|
|
var stop = line === "";
|
|
var curr = {
|
|
line,
|
|
ln,
|
|
pos,
|
|
dir: dir2
|
|
};
|
|
var last_valid = {
|
|
ln: curr.ln,
|
|
pos: curr.pos
|
|
};
|
|
var skip_empty_lines = curr.line === "";
|
|
nextChar2(cm2, curr);
|
|
while (curr.line !== null) {
|
|
last_valid.ln = curr.ln;
|
|
last_valid.pos = curr.pos;
|
|
if (curr.line === "" && !skip_empty_lines) {
|
|
return { ln: curr.ln, pos: curr.pos };
|
|
} else if (stop && curr.line !== "" && !isWhiteSpaceString(curr.line[curr.pos])) {
|
|
return { ln: curr.ln, pos: curr.pos };
|
|
} else if (isEndOfSentenceSymbol(curr.line[curr.pos]) && !stop && (curr.pos === curr.line.length - 1 || isWhiteSpaceString(curr.line[curr.pos + 1]))) {
|
|
stop = true;
|
|
}
|
|
nextChar2(cm2, curr);
|
|
}
|
|
var line = cm2.getLine(last_valid.ln);
|
|
last_valid.pos = 0;
|
|
for (var i = line.length - 1; i >= 0; --i) {
|
|
if (!isWhiteSpaceString(line[i])) {
|
|
last_valid.pos = i;
|
|
break;
|
|
}
|
|
}
|
|
return last_valid;
|
|
}
|
|
function reverse(cm2, ln, pos, dir2) {
|
|
var line = cm2.getLine(ln);
|
|
var curr = {
|
|
line,
|
|
ln,
|
|
pos,
|
|
dir: dir2
|
|
};
|
|
var last_valid_ln = curr.ln;
|
|
var last_valid_pos = null;
|
|
var skip_empty_lines = curr.line === "";
|
|
nextChar2(cm2, curr);
|
|
while (curr.line !== null) {
|
|
if (curr.line === "" && !skip_empty_lines) {
|
|
if (last_valid_pos !== null) {
|
|
return { ln: last_valid_ln, pos: last_valid_pos };
|
|
} else {
|
|
return { ln: curr.ln, pos: curr.pos };
|
|
}
|
|
} else if (isEndOfSentenceSymbol(curr.line[curr.pos]) && last_valid_pos !== null && !(curr.ln === last_valid_ln && curr.pos + 1 === last_valid_pos)) {
|
|
return { ln: last_valid_ln, pos: last_valid_pos };
|
|
} else if (curr.line !== "" && !isWhiteSpaceString(curr.line[curr.pos])) {
|
|
skip_empty_lines = false;
|
|
last_valid_ln = curr.ln;
|
|
last_valid_pos = curr.pos;
|
|
}
|
|
nextChar2(cm2, curr);
|
|
}
|
|
var line = cm2.getLine(last_valid_ln);
|
|
last_valid_pos = 0;
|
|
for (var i = 0; i < line.length; ++i) {
|
|
if (!isWhiteSpaceString(line[i])) {
|
|
last_valid_pos = i;
|
|
break;
|
|
}
|
|
}
|
|
return { ln: last_valid_ln, pos: last_valid_pos };
|
|
}
|
|
var curr_index = {
|
|
ln: cur2.line,
|
|
pos: cur2.ch
|
|
};
|
|
while (repeat > 0) {
|
|
if (dir < 0) {
|
|
curr_index = reverse(cm, curr_index.ln, curr_index.pos, dir);
|
|
} else {
|
|
curr_index = forward(cm, curr_index.ln, curr_index.pos, dir);
|
|
}
|
|
repeat--;
|
|
}
|
|
return new Pos2(curr_index.ln, curr_index.pos);
|
|
}
|
|
function selectCompanionObject(cm, head, symb, inclusive) {
|
|
var cur2 = head;
|
|
var bracketRegexp = {
|
|
"(": /[()]/,
|
|
")": /[()]/,
|
|
"[": /[[\]]/,
|
|
"]": /[[\]]/,
|
|
"{": /[{}]/,
|
|
"}": /[{}]/,
|
|
"<": /[<>]/,
|
|
">": /[<>]/
|
|
}[symb];
|
|
var openSym = {
|
|
"(": "(",
|
|
")": "(",
|
|
"[": "[",
|
|
"]": "[",
|
|
"{": "{",
|
|
"}": "{",
|
|
"<": "<",
|
|
">": "<"
|
|
}[symb];
|
|
var curChar = cm.getLine(cur2.line).charAt(cur2.ch);
|
|
var offset = curChar === openSym ? 1 : 0;
|
|
var startBracket = cm.scanForBracket(new Pos2(cur2.line, cur2.ch + offset), -1, void 0, { "bracketRegex": bracketRegexp });
|
|
var endBracket = cm.scanForBracket(new Pos2(cur2.line, cur2.ch + offset), 1, void 0, { "bracketRegex": bracketRegexp });
|
|
if (!startBracket || !endBracket) return null;
|
|
var start = startBracket.pos;
|
|
var end = endBracket.pos;
|
|
if (start.line == end.line && start.ch > end.ch || start.line > end.line) {
|
|
var tmp = start;
|
|
start = end;
|
|
end = tmp;
|
|
}
|
|
if (inclusive) {
|
|
end.ch += 1;
|
|
} else {
|
|
start.ch += 1;
|
|
}
|
|
return { start, end };
|
|
}
|
|
function findBeginningAndEnd(cm, head, symb, inclusive) {
|
|
var cur2 = copyCursor(head);
|
|
var line = cm.getLine(cur2.line);
|
|
var chars = line.split("");
|
|
var start, end, i, len;
|
|
var firstIndex = chars.indexOf(symb);
|
|
if (cur2.ch < firstIndex) {
|
|
cur2.ch = firstIndex;
|
|
} else if (firstIndex < cur2.ch && chars[cur2.ch] == symb) {
|
|
var stringAfter = /string/.test(cm.getTokenTypeAt(offsetCursor(head, 0, 1)));
|
|
var stringBefore = /string/.test(cm.getTokenTypeAt(head));
|
|
var isStringStart = stringAfter && !stringBefore;
|
|
if (!isStringStart) {
|
|
end = cur2.ch;
|
|
--cur2.ch;
|
|
}
|
|
}
|
|
if (chars[cur2.ch] == symb && !end) {
|
|
start = cur2.ch + 1;
|
|
} else {
|
|
for (i = cur2.ch; i > -1 && !start; i--) {
|
|
if (chars[i] == symb) {
|
|
start = i + 1;
|
|
}
|
|
}
|
|
}
|
|
if (start && !end) {
|
|
for (i = start, len = chars.length; i < len && !end; i++) {
|
|
if (chars[i] == symb) {
|
|
end = i;
|
|
}
|
|
}
|
|
}
|
|
if (!start || !end) {
|
|
return { start: cur2, end: cur2 };
|
|
}
|
|
if (inclusive) {
|
|
--start;
|
|
++end;
|
|
}
|
|
return {
|
|
start: new Pos2(cur2.line, start),
|
|
end: new Pos2(cur2.line, end)
|
|
};
|
|
}
|
|
defineOption("pcre", true, "boolean");
|
|
class SearchState {
|
|
constructor() {
|
|
this.highlightTimeout;
|
|
}
|
|
getQuery() {
|
|
return vimGlobalState.query;
|
|
}
|
|
setQuery(query) {
|
|
vimGlobalState.query = query;
|
|
}
|
|
getOverlay() {
|
|
return this.searchOverlay;
|
|
}
|
|
setOverlay(overlay) {
|
|
this.searchOverlay = overlay;
|
|
}
|
|
isReversed() {
|
|
return vimGlobalState.isReversed;
|
|
}
|
|
setReversed(reversed) {
|
|
vimGlobalState.isReversed = reversed;
|
|
}
|
|
getScrollbarAnnotate() {
|
|
return this.annotate;
|
|
}
|
|
setScrollbarAnnotate(annotate) {
|
|
this.annotate = annotate;
|
|
}
|
|
}
|
|
function getSearchState(cm) {
|
|
var vim2 = cm.state.vim;
|
|
return vim2.searchState_ || (vim2.searchState_ = new SearchState());
|
|
}
|
|
function splitBySlash(argString) {
|
|
return splitBySeparator(argString, "/");
|
|
}
|
|
function findUnescapedSlashes(argString) {
|
|
return findUnescapedSeparators(argString, "/");
|
|
}
|
|
function splitBySeparator(argString, separator) {
|
|
var slashes = findUnescapedSeparators(argString, separator) || [];
|
|
if (!slashes.length) return [];
|
|
var tokens = [];
|
|
if (slashes[0] !== 0) return;
|
|
for (var i = 0; i < slashes.length; i++) {
|
|
if (typeof slashes[i] == "number")
|
|
tokens.push(argString.substring(slashes[i] + 1, slashes[i + 1]));
|
|
}
|
|
return tokens;
|
|
}
|
|
function findUnescapedSeparators(str, separator) {
|
|
if (!separator)
|
|
separator = "/";
|
|
var escapeNextChar = false;
|
|
var slashes = [];
|
|
for (var i = 0; i < str.length; i++) {
|
|
var c = str.charAt(i);
|
|
if (!escapeNextChar && c == separator) {
|
|
slashes.push(i);
|
|
}
|
|
escapeNextChar = !escapeNextChar && c == "\\";
|
|
}
|
|
return slashes;
|
|
}
|
|
function translateRegex(str) {
|
|
var modes = {
|
|
V: "|(){+?*.[$^",
|
|
// verynomagic
|
|
M: "|(){+?*.[",
|
|
// nomagic
|
|
m: "|(){+?",
|
|
// magic
|
|
v: "<>"
|
|
// verymagic
|
|
};
|
|
var escapes = {
|
|
">": "(?<=[\\w])(?=[^\\w]|$)",
|
|
"<": "(?<=[^\\w]|^)(?=[\\w])"
|
|
};
|
|
var specials = modes.m;
|
|
var regex = str.replace(/\\.|[\[|(){+*?.$^<>]/g, function(match) {
|
|
if (match[0] === "\\") {
|
|
var nextChar2 = match[1];
|
|
if (nextChar2 === "}" || specials.indexOf(nextChar2) != -1) {
|
|
return nextChar2;
|
|
}
|
|
if (nextChar2 in modes) {
|
|
specials = modes[nextChar2];
|
|
return "";
|
|
}
|
|
if (nextChar2 in escapes) {
|
|
return escapes[nextChar2];
|
|
}
|
|
return match;
|
|
} else {
|
|
if (specials.indexOf(match) != -1) {
|
|
return escapes[match] || "\\" + match;
|
|
}
|
|
return match;
|
|
}
|
|
});
|
|
var i = regex.indexOf("\\zs");
|
|
if (i != -1) {
|
|
regex = "(?<=" + regex.slice(0, i) + ")" + regex.slice(i + 3);
|
|
}
|
|
i = regex.indexOf("\\ze");
|
|
if (i != -1) {
|
|
regex = regex.slice(0, i) + "(?=" + regex.slice(i + 3) + ")";
|
|
}
|
|
return regex;
|
|
}
|
|
var charUnescapes = { "\\n": "\n", "\\r": "\r", "\\t": " " };
|
|
function translateRegexReplace(str) {
|
|
var escapeNextChar = false;
|
|
var out = [];
|
|
for (var i = -1; i < str.length; i++) {
|
|
var c = str.charAt(i) || "";
|
|
var n = str.charAt(i + 1) || "";
|
|
if (charUnescapes[c + n]) {
|
|
out.push(charUnescapes[c + n]);
|
|
i++;
|
|
} else if (escapeNextChar) {
|
|
out.push(c);
|
|
escapeNextChar = false;
|
|
} else {
|
|
if (c === "\\") {
|
|
escapeNextChar = true;
|
|
if (isNumber(n) || n === "$") {
|
|
out.push("$");
|
|
} else if (n !== "/" && n !== "\\") {
|
|
out.push("\\");
|
|
}
|
|
} else {
|
|
if (c === "$") {
|
|
out.push("$");
|
|
}
|
|
out.push(c);
|
|
if (n === "/") {
|
|
out.push("\\");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return out.join("");
|
|
}
|
|
var unescapes = { "\\/": "/", "\\\\": "\\", "\\n": "\n", "\\r": "\r", "\\t": " ", "\\&": "&" };
|
|
function unescapeRegexReplace(str) {
|
|
var stream = new CM.StringStream(str);
|
|
var output = [];
|
|
while (!stream.eol()) {
|
|
while (stream.peek() && stream.peek() != "\\") {
|
|
output.push(stream.next());
|
|
}
|
|
var matched = false;
|
|
for (var matcher in unescapes) {
|
|
if (stream.match(matcher, true)) {
|
|
matched = true;
|
|
output.push(unescapes[matcher]);
|
|
break;
|
|
}
|
|
}
|
|
if (!matched) {
|
|
output.push(stream.next());
|
|
}
|
|
}
|
|
return output.join("");
|
|
}
|
|
function parseQuery(query, ignoreCase, smartCase) {
|
|
var lastSearchRegister = vimGlobalState.registerController.getRegister("/");
|
|
lastSearchRegister.setText(query);
|
|
var slashes = findUnescapedSlashes(query);
|
|
var regexPart;
|
|
var forceIgnoreCase;
|
|
if (!slashes.length) {
|
|
regexPart = query;
|
|
} else {
|
|
regexPart = query.substring(0, slashes[0]);
|
|
var flagsPart = query.substring(slashes[0]);
|
|
forceIgnoreCase = flagsPart.indexOf("i") != -1;
|
|
}
|
|
if (!regexPart) {
|
|
return null;
|
|
}
|
|
if (!getOption("pcre")) {
|
|
regexPart = translateRegex(regexPart);
|
|
}
|
|
if (smartCase) {
|
|
ignoreCase = /^[^A-Z]*$/.test(regexPart);
|
|
}
|
|
var regexp = new RegExp(
|
|
regexPart,
|
|
ignoreCase || forceIgnoreCase ? "im" : "m"
|
|
);
|
|
return regexp;
|
|
}
|
|
function dom(n) {
|
|
if (typeof n === "string") n = document.createElement(n);
|
|
for (var i = 1; i < arguments.length; i++) {
|
|
var a = arguments[i];
|
|
if (!a) continue;
|
|
if (typeof a !== "object") a = document.createTextNode(a);
|
|
if (a.nodeType) n.appendChild(a);
|
|
else for (var key in a) {
|
|
if (!Object.prototype.hasOwnProperty.call(a, key)) continue;
|
|
if (key[0] === "$") n.style[key.slice(1)] = a[key];
|
|
else if (typeof a[key] == "function") n[key] = a[key];
|
|
else n.setAttribute(key, a[key]);
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
function showConfirm(cm, template, long) {
|
|
var pre = dom("div", { $color: "red", $whiteSpace: "pre", class: "cm-vim-message" }, template);
|
|
if (cm.openNotification) {
|
|
if (long) {
|
|
pre = dom("div", {}, pre, dom("div", {}, "Press ENTER or type command to continue"));
|
|
if (cm.state.closeVimNotification) {
|
|
cm.state.closeVimNotification();
|
|
}
|
|
cm.state.closeVimNotification = cm.openNotification(pre, { bottom: true, duration: 0 });
|
|
} else {
|
|
cm.openNotification(pre, { bottom: true, duration: 15e3 });
|
|
}
|
|
} else {
|
|
alert(pre.innerText);
|
|
}
|
|
}
|
|
function makePrompt(prefix, desc) {
|
|
return dom(
|
|
"div",
|
|
{ $display: "flex", $flex: 1 },
|
|
dom(
|
|
"span",
|
|
{ $fontFamily: "monospace", $whiteSpace: "pre", $flex: 1, $display: "flex" },
|
|
prefix,
|
|
dom("input", {
|
|
type: "text",
|
|
autocorrect: "off",
|
|
autocapitalize: "off",
|
|
spellcheck: "false",
|
|
$flex: 1
|
|
})
|
|
),
|
|
desc && dom("span", { $color: "#888" }, desc)
|
|
);
|
|
}
|
|
function showPrompt(cm, options2) {
|
|
var _a;
|
|
if (keyToKeyStack.length) {
|
|
if (!options2.value) options2.value = "";
|
|
virtualPrompt = options2;
|
|
return;
|
|
}
|
|
var template = makePrompt(options2.prefix, options2.desc);
|
|
if (cm.openDialog) {
|
|
cm.openDialog(template, options2.onClose, {
|
|
onKeyDown: options2.onKeyDown,
|
|
onKeyUp: options2.onKeyUp,
|
|
bottom: true,
|
|
selectValueOnOpen: false,
|
|
value: options2.value
|
|
});
|
|
} else {
|
|
var shortText = "";
|
|
if (typeof options2.prefix != "string" && options2.prefix) shortText += options2.prefix.textContent;
|
|
if (options2.desc) shortText += " " + options2.desc;
|
|
(_a = options2.onClose) == null ? void 0 : _a.call(options2, prompt(shortText, ""));
|
|
}
|
|
}
|
|
function regexEqual(r1, r2) {
|
|
if (r1 instanceof RegExp && r2 instanceof RegExp) {
|
|
return r1.flags == r2.flags && r1.source == r2.source;
|
|
}
|
|
return false;
|
|
}
|
|
function updateSearchQuery(cm, rawQuery, ignoreCase, smartCase) {
|
|
if (!rawQuery) {
|
|
return;
|
|
}
|
|
var state = getSearchState(cm);
|
|
var query = parseQuery(rawQuery, !!ignoreCase, !!smartCase);
|
|
if (!query) {
|
|
return;
|
|
}
|
|
highlightSearchMatches(cm, query);
|
|
if (regexEqual(query, state.getQuery())) {
|
|
return query;
|
|
}
|
|
state.setQuery(query);
|
|
return query;
|
|
}
|
|
function searchOverlay(query) {
|
|
if (query.source.charAt(0) == "^") {
|
|
var matchSol = true;
|
|
}
|
|
return {
|
|
token: function(stream) {
|
|
if (matchSol && !stream.sol()) {
|
|
stream.skipToEnd();
|
|
return;
|
|
}
|
|
var match = stream.match(query, false);
|
|
if (match) {
|
|
if (match[0].length == 0) {
|
|
stream.next();
|
|
return "searching";
|
|
}
|
|
if (!stream.sol()) {
|
|
stream.backUp(1);
|
|
if (!query.exec(stream.next() + match[0])) {
|
|
stream.next();
|
|
return null;
|
|
}
|
|
}
|
|
stream.match(query);
|
|
return "searching";
|
|
}
|
|
while (!stream.eol()) {
|
|
stream.next();
|
|
if (stream.match(query, false)) break;
|
|
}
|
|
},
|
|
query
|
|
};
|
|
}
|
|
var highlightTimeout = 0;
|
|
function highlightSearchMatches(cm, query) {
|
|
clearTimeout(highlightTimeout);
|
|
var searchState = getSearchState(cm);
|
|
searchState.highlightTimeout = highlightTimeout;
|
|
highlightTimeout = setTimeout(function() {
|
|
if (!cm.state.vim) return;
|
|
var searchState2 = getSearchState(cm);
|
|
searchState2.highlightTimeout = void 0;
|
|
var overlay = searchState2.getOverlay();
|
|
if (!overlay || query != overlay.query) {
|
|
if (overlay) {
|
|
cm.removeOverlay(overlay);
|
|
}
|
|
overlay = searchOverlay(query);
|
|
cm.addOverlay(overlay);
|
|
if (cm.showMatchesOnScrollbar) {
|
|
if (searchState2.getScrollbarAnnotate()) {
|
|
searchState2.getScrollbarAnnotate().clear();
|
|
}
|
|
searchState2.setScrollbarAnnotate(cm.showMatchesOnScrollbar(query));
|
|
}
|
|
searchState2.setOverlay(overlay);
|
|
}
|
|
}, 50);
|
|
}
|
|
function findNext(cm, prev, query, repeat) {
|
|
return cm.operation(function() {
|
|
if (repeat === void 0) {
|
|
repeat = 1;
|
|
}
|
|
var pos = cm.getCursor();
|
|
var cursor = cm.getSearchCursor(query, pos);
|
|
for (var i = 0; i < repeat; i++) {
|
|
var found = cursor.find(prev);
|
|
if (i == 0 && found && cursorEqual(cursor.from(), pos)) {
|
|
var lastEndPos = prev ? cursor.from() : cursor.to();
|
|
found = cursor.find(prev);
|
|
if (found && !found[0] && cursorEqual(cursor.from(), lastEndPos)) {
|
|
if (cm.getLine(lastEndPos.line).length == lastEndPos.ch)
|
|
found = cursor.find(prev);
|
|
}
|
|
}
|
|
if (!found) {
|
|
cursor = cm.getSearchCursor(
|
|
query,
|
|
// @ts-ignore
|
|
prev ? new Pos2(cm.lastLine()) : new Pos2(cm.firstLine(), 0)
|
|
);
|
|
if (!cursor.find(prev)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
return cursor.from();
|
|
});
|
|
}
|
|
function findNextFromAndToInclusive(cm, prev, query, repeat, vim2) {
|
|
return cm.operation(function() {
|
|
if (repeat === void 0) {
|
|
repeat = 1;
|
|
}
|
|
var pos = cm.getCursor();
|
|
var cursor = cm.getSearchCursor(query, pos);
|
|
var found = cursor.find(!prev);
|
|
if (!vim2.visualMode && found && cursorEqual(cursor.from(), pos)) {
|
|
cursor.find(!prev);
|
|
}
|
|
for (var i = 0; i < repeat; i++) {
|
|
found = cursor.find(prev);
|
|
if (!found) {
|
|
cursor = cm.getSearchCursor(
|
|
query,
|
|
// @ts-ignore
|
|
prev ? new Pos2(cm.lastLine()) : new Pos2(cm.firstLine(), 0)
|
|
);
|
|
if (!cursor.find(prev)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
var from = cursor.from();
|
|
var to = cursor.to();
|
|
return from && to && [from, to];
|
|
});
|
|
}
|
|
function clearSearchHighlight(cm) {
|
|
var state = getSearchState(cm);
|
|
if (state.highlightTimeout) {
|
|
clearTimeout(state.highlightTimeout);
|
|
state.highlightTimeout = void 0;
|
|
}
|
|
cm.removeOverlay(getSearchState(cm).getOverlay());
|
|
state.setOverlay(null);
|
|
if (state.getScrollbarAnnotate()) {
|
|
state.getScrollbarAnnotate().clear();
|
|
state.setScrollbarAnnotate(null);
|
|
}
|
|
}
|
|
function isInRange(pos, start, end) {
|
|
if (typeof pos != "number") {
|
|
pos = pos.line;
|
|
}
|
|
if (start instanceof Array) {
|
|
return inArray(pos, start);
|
|
} else {
|
|
if (typeof end == "number") {
|
|
return pos >= start && pos <= end;
|
|
} else {
|
|
return pos == start;
|
|
}
|
|
}
|
|
}
|
|
function getUserVisibleLines(cm) {
|
|
var scrollInfo = cm.getScrollInfo();
|
|
var occludeToleranceTop = 6;
|
|
var occludeToleranceBottom = 10;
|
|
var from = cm.coordsChar({ left: 0, top: occludeToleranceTop + scrollInfo.top }, "local");
|
|
var bottomY = scrollInfo.clientHeight - occludeToleranceBottom + scrollInfo.top;
|
|
var to = cm.coordsChar({ left: 0, top: bottomY }, "local");
|
|
return { top: from.line, bottom: to.line };
|
|
}
|
|
function getMarkPos(cm, vim2, markName) {
|
|
if (markName == "'" || markName == "`") {
|
|
return vimGlobalState.jumpList.find(cm, -1) || new Pos2(0, 0);
|
|
} else if (markName == ".") {
|
|
return getLastEditPos(cm);
|
|
}
|
|
var mark = vim2.marks[markName];
|
|
return mark && mark.find();
|
|
}
|
|
function getLastEditPos(cm) {
|
|
if (cm.getLastEditEnd) {
|
|
return cm.getLastEditEnd();
|
|
}
|
|
var done = (
|
|
/**@type{any}*/
|
|
cm.doc.history.done
|
|
);
|
|
for (var i = done.length; i--; ) {
|
|
if (done[i].changes) {
|
|
return copyCursor(done[i].changes[0].to);
|
|
}
|
|
}
|
|
}
|
|
class ExCommandDispatcher {
|
|
constructor() {
|
|
this.commandMap_;
|
|
this.buildCommandMap_();
|
|
}
|
|
/**
|
|
* @arg {CodeMirrorV} cm
|
|
* @arg {string} input
|
|
* @arg {{ callback: () => void; } | undefined} [opt_params]
|
|
*/
|
|
processCommand(cm, input, opt_params) {
|
|
var that = this;
|
|
cm.operation(function() {
|
|
if (cm.curOp) cm.curOp.isVimOp = true;
|
|
that._processCommand(cm, input, opt_params);
|
|
});
|
|
}
|
|
/**
|
|
* @arg {CodeMirrorV} cm
|
|
* @arg {string} input
|
|
* @arg {{ callback?: () => void; input?: string, line?: string, commandName?: string } } [opt_params]
|
|
*/
|
|
_processCommand(cm, input, opt_params) {
|
|
var vim2 = cm.state.vim;
|
|
var commandHistoryRegister = vimGlobalState.registerController.getRegister(":");
|
|
var previousCommand = commandHistoryRegister.toString();
|
|
var inputStream = new CM.StringStream(input);
|
|
commandHistoryRegister.setText(input);
|
|
var params = opt_params || {};
|
|
params.input = input;
|
|
try {
|
|
this.parseInput_(cm, inputStream, params);
|
|
} catch (e) {
|
|
showConfirm(cm, e + "");
|
|
throw e;
|
|
}
|
|
if (vim2.visualMode) {
|
|
exitVisualMode(cm);
|
|
}
|
|
var command;
|
|
var commandName;
|
|
if (!params.commandName) {
|
|
if (params.line !== void 0) {
|
|
commandName = "move";
|
|
}
|
|
} else {
|
|
command = this.matchCommand_(params.commandName);
|
|
if (command) {
|
|
commandName = command.name;
|
|
if (command.excludeFromCommandHistory) {
|
|
commandHistoryRegister.setText(previousCommand);
|
|
}
|
|
this.parseCommandArgs_(inputStream, params, command);
|
|
if (command.type == "exToKey") {
|
|
doKeyToKey(cm, command.toKeys || "", command);
|
|
return;
|
|
} else if (command.type == "exToEx") {
|
|
this.processCommand(cm, command.toInput || "");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (!commandName) {
|
|
showConfirm(cm, 'Not an editor command ":' + input + '"');
|
|
return;
|
|
}
|
|
try {
|
|
exCommands[commandName](cm, params);
|
|
if ((!command || !command.possiblyAsync) && params.callback) {
|
|
params.callback();
|
|
}
|
|
} catch (e) {
|
|
showConfirm(cm, e + "");
|
|
throw e;
|
|
}
|
|
}
|
|
/**
|
|
* @param {CodeMirrorV} cm
|
|
* @param {import("@codemirror/language").StringStream} inputStream
|
|
* @param {{ callback?: (() => void) | undefined; input?: string | undefined; line?: any; commandName?: any; lineEnd?: any; selectionLine?: any; selectionLineEnd?: any; }} result
|
|
*/
|
|
parseInput_(cm, inputStream, result) {
|
|
var _a, _b;
|
|
inputStream.eatWhile(":");
|
|
if (inputStream.eat("%")) {
|
|
result.line = cm.firstLine();
|
|
result.lineEnd = cm.lastLine();
|
|
} else {
|
|
result.line = this.parseLineSpec_(cm, inputStream);
|
|
if (result.line !== void 0 && inputStream.eat(",")) {
|
|
result.lineEnd = this.parseLineSpec_(cm, inputStream);
|
|
}
|
|
}
|
|
if (result.line == void 0) {
|
|
if (cm.state.vim.visualMode) {
|
|
result.selectionLine = (_a = getMarkPos(cm, cm.state.vim, "<")) == null ? void 0 : _a.line;
|
|
result.selectionLineEnd = (_b = getMarkPos(cm, cm.state.vim, ">")) == null ? void 0 : _b.line;
|
|
} else {
|
|
result.selectionLine = cm.getCursor().line;
|
|
}
|
|
} else {
|
|
result.selectionLine = result.line;
|
|
result.selectionLineEnd = result.lineEnd;
|
|
}
|
|
var commandMatch2 = inputStream.match(/^(\w+|!!|@@|[!#&*<=>@~])/);
|
|
if (commandMatch2) {
|
|
result.commandName = commandMatch2[1];
|
|
} else {
|
|
result.commandName = (inputStream.match(/.*/) || [""])[0];
|
|
}
|
|
return result;
|
|
}
|
|
/**
|
|
* @param {CodeMirrorV} cm
|
|
* @param {import("@codemirror/language").StringStream} inputStream
|
|
*/
|
|
parseLineSpec_(cm, inputStream) {
|
|
var numberMatch = inputStream.match(/^(\d+)/);
|
|
if (numberMatch) {
|
|
return parseInt(numberMatch[1], 10) - 1;
|
|
}
|
|
switch (inputStream.next()) {
|
|
case ".":
|
|
return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);
|
|
case "$":
|
|
return this.parseLineSpecOffset_(inputStream, cm.lastLine());
|
|
case "'":
|
|
var markName = inputStream.next() || "";
|
|
var markPos = getMarkPos(cm, cm.state.vim, markName);
|
|
if (!markPos) throw new Error("Mark not set");
|
|
return this.parseLineSpecOffset_(inputStream, markPos.line);
|
|
case "-":
|
|
case "+":
|
|
inputStream.backUp(1);
|
|
return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);
|
|
default:
|
|
inputStream.backUp(1);
|
|
return void 0;
|
|
}
|
|
}
|
|
/**
|
|
* @param {string | import("@codemirror/language").StringStream} inputStream
|
|
* @param {number} line
|
|
*/
|
|
parseLineSpecOffset_(inputStream, line) {
|
|
var offsetMatch = inputStream.match(/^([+-])?(\d+)/);
|
|
if (offsetMatch) {
|
|
var offset = parseInt(offsetMatch[2], 10);
|
|
if (offsetMatch[1] == "-") {
|
|
line -= offset;
|
|
} else {
|
|
line += offset;
|
|
}
|
|
}
|
|
return line;
|
|
}
|
|
/**
|
|
* @param {import("@codemirror/language").StringStream} inputStream
|
|
* @param {import("./types").exCommandArgs} params
|
|
* @param {import("./types").exCommandDefinition} command
|
|
*/
|
|
parseCommandArgs_(inputStream, params, command) {
|
|
var _a;
|
|
if (inputStream.eol()) {
|
|
return;
|
|
}
|
|
params.argString = (_a = inputStream.match(/.*/)) == null ? void 0 : _a[0];
|
|
var delim = command.argDelimiter || /\s+/;
|
|
var args = trim(params.argString || "").split(delim);
|
|
if (args.length && args[0]) {
|
|
params.args = args;
|
|
}
|
|
}
|
|
/**
|
|
* @arg {string} commandName
|
|
*/
|
|
matchCommand_(commandName) {
|
|
for (var i = commandName.length; i > 0; i--) {
|
|
var prefix = commandName.substring(0, i);
|
|
if (this.commandMap_[prefix]) {
|
|
var command = this.commandMap_[prefix];
|
|
if (command.name.indexOf(commandName) === 0) {
|
|
return command;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
buildCommandMap_() {
|
|
this.commandMap_ = {};
|
|
for (var i = 0; i < defaultExCommandMap.length; i++) {
|
|
var command = defaultExCommandMap[i];
|
|
var key = command.shortName || command.name;
|
|
this.commandMap_[key] = command;
|
|
}
|
|
}
|
|
/**@type {(lhs: string, rhs: string, ctx: string|void, noremap?: boolean) => void} */
|
|
map(lhs, rhs, ctx, noremap2) {
|
|
if (lhs != ":" && lhs.charAt(0) == ":") {
|
|
if (ctx) {
|
|
throw Error("Mode not supported for ex mappings");
|
|
}
|
|
var commandName = lhs.substring(1);
|
|
if (rhs != ":" && rhs.charAt(0) == ":") {
|
|
this.commandMap_[commandName] = {
|
|
name: commandName,
|
|
type: "exToEx",
|
|
toInput: rhs.substring(1),
|
|
user: true
|
|
};
|
|
} else {
|
|
this.commandMap_[commandName] = {
|
|
name: commandName,
|
|
type: "exToKey",
|
|
toKeys: rhs,
|
|
user: true
|
|
};
|
|
}
|
|
} else {
|
|
var mapping = {
|
|
keys: lhs,
|
|
type: "keyToKey",
|
|
toKeys: rhs,
|
|
noremap: !!noremap2
|
|
};
|
|
if (ctx) {
|
|
mapping.context = ctx;
|
|
}
|
|
_mapCommand(mapping);
|
|
}
|
|
}
|
|
/**@type {(lhs: string, ctx: string) => boolean|void} */
|
|
unmap(lhs, ctx) {
|
|
if (lhs != ":" && lhs.charAt(0) == ":") {
|
|
if (ctx) {
|
|
throw Error("Mode not supported for ex mappings");
|
|
}
|
|
var commandName = lhs.substring(1);
|
|
if (this.commandMap_[commandName] && this.commandMap_[commandName].user) {
|
|
delete this.commandMap_[commandName];
|
|
return true;
|
|
}
|
|
} else {
|
|
var keys2 = lhs;
|
|
for (var i = 0; i < defaultKeymap2.length; i++) {
|
|
if (keys2 == defaultKeymap2[i].keys && defaultKeymap2[i].context === ctx) {
|
|
defaultKeymap2.splice(i, 1);
|
|
removeUsedKeys(keys2);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var exCommands = {
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
colorscheme: function(cm, params) {
|
|
if (!params.args || params.args.length < 1) {
|
|
showConfirm(cm, cm.getOption("theme"));
|
|
return;
|
|
}
|
|
cm.setOption("theme", params.args[0]);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params @arg {'insert'|'normal'|string} [ctx] @arg {boolean} [defaultOnly]*/
|
|
map: function(cm, params, ctx, defaultOnly) {
|
|
var mapArgs = params.args;
|
|
if (!mapArgs || mapArgs.length < 2) {
|
|
if (cm) {
|
|
showConfirm(cm, "Invalid mapping: " + params.input);
|
|
}
|
|
return;
|
|
}
|
|
exCommandDispatcher.map(mapArgs[0], mapArgs[1], ctx, defaultOnly);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
imap: function(cm, params) {
|
|
this.map(cm, params, "insert");
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
nmap: function(cm, params) {
|
|
this.map(cm, params, "normal");
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
vmap: function(cm, params) {
|
|
this.map(cm, params, "visual");
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
omap: function(cm, params) {
|
|
this.map(cm, params, "operatorPending");
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
noremap: function(cm, params) {
|
|
this.map(cm, params, void 0, true);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
inoremap: function(cm, params) {
|
|
this.map(cm, params, "insert", true);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
nnoremap: function(cm, params) {
|
|
this.map(cm, params, "normal", true);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
vnoremap: function(cm, params) {
|
|
this.map(cm, params, "visual", true);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
onoremap: function(cm, params) {
|
|
this.map(cm, params, "operatorPending", true);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params @arg {string} ctx*/
|
|
unmap: function(cm, params, ctx) {
|
|
var mapArgs = params.args;
|
|
if (!mapArgs || mapArgs.length < 1 || !exCommandDispatcher.unmap(mapArgs[0], ctx)) {
|
|
if (cm) {
|
|
showConfirm(cm, "No such mapping: " + params.input);
|
|
}
|
|
}
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
mapclear: function(cm, params) {
|
|
vimApi.mapclear();
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
imapclear: function(cm, params) {
|
|
vimApi.mapclear("insert");
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
nmapclear: function(cm, params) {
|
|
vimApi.mapclear("normal");
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
vmapclear: function(cm, params) {
|
|
vimApi.mapclear("visual");
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
omapclear: function(cm, params) {
|
|
vimApi.mapclear("operatorPending");
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
move: function(cm, params) {
|
|
commandDispatcher.processCommand(cm, cm.state.vim, {
|
|
keys: "",
|
|
type: "motion",
|
|
motion: "moveToLineOrEdgeOfDocument",
|
|
motionArgs: { forward: false, explicitRepeat: true, linewise: true },
|
|
repeatOverride: params.line + 1
|
|
});
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
set: function(cm, params) {
|
|
var setArgs = params.args;
|
|
var setCfg = params.setCfg || {};
|
|
if (!setArgs || setArgs.length < 1) {
|
|
if (cm) {
|
|
showConfirm(cm, "Invalid mapping: " + params.input);
|
|
}
|
|
return;
|
|
}
|
|
var expr = setArgs[0].split("=");
|
|
var optionName = expr.shift() || "";
|
|
var value = expr.length > 0 ? expr.join("=") : void 0;
|
|
var forceGet = false;
|
|
var forceToggle = false;
|
|
if (optionName.charAt(optionName.length - 1) == "?") {
|
|
if (value) {
|
|
throw Error("Trailing characters: " + params.argString);
|
|
}
|
|
optionName = optionName.substring(0, optionName.length - 1);
|
|
forceGet = true;
|
|
} else if (optionName.charAt(optionName.length - 1) == "!") {
|
|
optionName = optionName.substring(0, optionName.length - 1);
|
|
forceToggle = true;
|
|
}
|
|
if (value === void 0 && optionName.substring(0, 2) == "no") {
|
|
optionName = optionName.substring(2);
|
|
value = false;
|
|
}
|
|
var optionIsBoolean = options[optionName] && options[optionName].type == "boolean";
|
|
if (optionIsBoolean) {
|
|
if (forceToggle) {
|
|
value = !getOption(optionName, cm, setCfg);
|
|
} else if (value == void 0) {
|
|
value = true;
|
|
}
|
|
}
|
|
if (!optionIsBoolean && value === void 0 || forceGet) {
|
|
var oldValue = getOption(optionName, cm, setCfg);
|
|
if (oldValue instanceof Error) {
|
|
showConfirm(cm, oldValue.message);
|
|
} else if (oldValue === true || oldValue === false) {
|
|
showConfirm(cm, " " + (oldValue ? "" : "no") + optionName);
|
|
} else {
|
|
showConfirm(cm, " " + optionName + "=" + oldValue);
|
|
}
|
|
} else {
|
|
var setOptionReturn = setOption(optionName, value, cm, setCfg);
|
|
if (setOptionReturn instanceof Error) {
|
|
showConfirm(cm, setOptionReturn.message);
|
|
}
|
|
}
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
setlocal: function(cm, params) {
|
|
params.setCfg = { scope: "local" };
|
|
this.set(cm, params);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
setglobal: function(cm, params) {
|
|
params.setCfg = { scope: "global" };
|
|
this.set(cm, params);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
registers: function(cm, params) {
|
|
var regArgs = params.args;
|
|
var registers = vimGlobalState.registerController.registers;
|
|
var regInfo = "----------Registers----------\n\n";
|
|
if (!regArgs) {
|
|
for (var registerName in registers) {
|
|
var text = registers[registerName].toString();
|
|
if (text.length) {
|
|
regInfo += '"' + registerName + " " + text + "\n";
|
|
}
|
|
}
|
|
} else {
|
|
var registerNames = regArgs.join("");
|
|
for (var i = 0; i < registerNames.length; i++) {
|
|
var registerName = registerNames.charAt(i);
|
|
if (!vimGlobalState.registerController.isValidRegister(registerName)) {
|
|
continue;
|
|
}
|
|
var register = registers[registerName] || new Register();
|
|
regInfo += '"' + registerName + " " + register.toString() + "\n";
|
|
}
|
|
}
|
|
showConfirm(cm, regInfo, true);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
marks: function(cm, params) {
|
|
var filterArgs = params.args;
|
|
var marks = cm.state.vim.marks;
|
|
var regInfo = "-----------Marks-----------\nmark line col\n\n";
|
|
if (!filterArgs) {
|
|
for (var name in marks) {
|
|
var marker = marks[name] && marks[name].find();
|
|
if (marker) {
|
|
regInfo += name + " " + marker.line + " " + marker.ch + "\n";
|
|
}
|
|
}
|
|
} else {
|
|
var registerNames = filterArgs.join("");
|
|
for (var i = 0; i < registerNames.length; i++) {
|
|
var name = registerNames.charAt(i);
|
|
var marker = marks[name] && marks[name].find();
|
|
if (marker) {
|
|
regInfo += name + " " + marker.line + " " + marker.ch + "\n";
|
|
}
|
|
}
|
|
}
|
|
showConfirm(cm, regInfo, true);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
sort: function(cm, params) {
|
|
var reverse, ignoreCase, unique, number, pattern;
|
|
function parseArgs() {
|
|
if (params.argString) {
|
|
var args = new CM.StringStream(params.argString);
|
|
if (args.eat("!")) {
|
|
reverse = true;
|
|
}
|
|
if (args.eol()) {
|
|
return;
|
|
}
|
|
if (!args.eatSpace()) {
|
|
return "Invalid arguments";
|
|
}
|
|
var opts = args.match(/([dinuox]+)?\s*(\/.+\/)?\s*/);
|
|
if (!opts || !args.eol()) {
|
|
return "Invalid arguments";
|
|
}
|
|
if (opts[1]) {
|
|
ignoreCase = opts[1].indexOf("i") != -1;
|
|
unique = opts[1].indexOf("u") != -1;
|
|
var decimal = opts[1].indexOf("d") != -1 || opts[1].indexOf("n") != -1;
|
|
var hex = opts[1].indexOf("x") != -1;
|
|
var octal = opts[1].indexOf("o") != -1;
|
|
if (Number(decimal) + Number(hex) + Number(octal) > 1) {
|
|
return "Invalid arguments";
|
|
}
|
|
number = decimal && "decimal" || hex && "hex" || octal && "octal";
|
|
}
|
|
if (opts[2]) {
|
|
pattern = new RegExp(opts[2].substr(1, opts[2].length - 2), ignoreCase ? "i" : "");
|
|
}
|
|
}
|
|
}
|
|
var err = parseArgs();
|
|
if (err) {
|
|
showConfirm(cm, err + ": " + params.argString);
|
|
return;
|
|
}
|
|
var lineStart = params.line || cm.firstLine();
|
|
var lineEnd = params.lineEnd || params.line || cm.lastLine();
|
|
if (lineStart == lineEnd) {
|
|
return;
|
|
}
|
|
var curStart = new Pos2(lineStart, 0);
|
|
var curEnd = new Pos2(lineEnd, lineLength(cm, lineEnd));
|
|
var text = cm.getRange(curStart, curEnd).split("\n");
|
|
var numberRegex2 = number == "decimal" ? /(-?)([\d]+)/ : number == "hex" ? /(-?)(?:0x)?([0-9a-f]+)/i : number == "octal" ? /([0-7]+)/ : null;
|
|
var radix = number == "decimal" ? 10 : number == "hex" ? 16 : number == "octal" ? 8 : void 0;
|
|
var numPart = [], textPart = [];
|
|
if (number || pattern) {
|
|
for (var i = 0; i < text.length; i++) {
|
|
var matchPart = pattern ? text[i].match(pattern) : null;
|
|
if (matchPart && matchPart[0] != "") {
|
|
numPart.push(matchPart);
|
|
} else if (numberRegex2 && numberRegex2.exec(text[i])) {
|
|
numPart.push(text[i]);
|
|
} else {
|
|
textPart.push(text[i]);
|
|
}
|
|
}
|
|
} else {
|
|
textPart = text;
|
|
}
|
|
function compareFn(a, b) {
|
|
if (reverse) {
|
|
var tmp;
|
|
tmp = a;
|
|
a = b;
|
|
b = tmp;
|
|
}
|
|
if (ignoreCase) {
|
|
a = a.toLowerCase();
|
|
b = b.toLowerCase();
|
|
}
|
|
var amatch = numberRegex2 && numberRegex2.exec(a);
|
|
var bmatch = numberRegex2 && numberRegex2.exec(b);
|
|
if (!amatch || !bmatch) {
|
|
return a < b ? -1 : 1;
|
|
}
|
|
var anum = parseInt((amatch[1] + amatch[2]).toLowerCase(), radix);
|
|
var bnum = parseInt((bmatch[1] + bmatch[2]).toLowerCase(), radix);
|
|
return anum - bnum;
|
|
}
|
|
function comparePatternFn(a, b) {
|
|
if (reverse) {
|
|
var tmp;
|
|
tmp = a;
|
|
a = b;
|
|
b = tmp;
|
|
}
|
|
if (ignoreCase) {
|
|
a[0] = a[0].toLowerCase();
|
|
b[0] = b[0].toLowerCase();
|
|
}
|
|
return a[0] < b[0] ? -1 : 1;
|
|
}
|
|
numPart.sort(pattern ? comparePatternFn : compareFn);
|
|
if (pattern) {
|
|
for (var i = 0; i < numPart.length; i++) {
|
|
numPart[i] = numPart[i].input;
|
|
}
|
|
} else if (!number) {
|
|
textPart.sort(compareFn);
|
|
}
|
|
text = !reverse ? textPart.concat(numPart) : numPart.concat(textPart);
|
|
if (unique) {
|
|
var textOld = text;
|
|
var lastLine;
|
|
text = [];
|
|
for (var i = 0; i < textOld.length; i++) {
|
|
if (textOld[i] != lastLine) {
|
|
text.push(textOld[i]);
|
|
}
|
|
lastLine = textOld[i];
|
|
}
|
|
}
|
|
cm.replaceRange(text.join("\n"), curStart, curEnd);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
vglobal: function(cm, params) {
|
|
this.global(cm, params);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
normal: function(cm, params) {
|
|
var noremap2 = false;
|
|
var argString = params.argString;
|
|
if (argString && argString[0] == "!") {
|
|
argString = argString.slice(1);
|
|
noremap2 = true;
|
|
}
|
|
argString = argString.trimStart();
|
|
if (!argString) {
|
|
showConfirm(cm, "Argument is required.");
|
|
return;
|
|
}
|
|
var line = params.line;
|
|
if (typeof line == "number") {
|
|
var lineEnd = isNaN(params.lineEnd) ? line : params.lineEnd;
|
|
for (var i = line; i <= lineEnd; i++) {
|
|
cm.setCursor(i, 0);
|
|
doKeyToKey(cm, params.argString.trimStart(), { noremap: noremap2 });
|
|
if (cm.state.vim.insertMode) {
|
|
exitInsertMode(cm, true);
|
|
}
|
|
}
|
|
} else {
|
|
doKeyToKey(cm, params.argString.trimStart(), { noremap: noremap2 });
|
|
if (cm.state.vim.insertMode) {
|
|
exitInsertMode(cm, true);
|
|
}
|
|
}
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
global: function(cm, params) {
|
|
var argString = params.argString;
|
|
if (!argString) {
|
|
showConfirm(cm, "Regular Expression missing from global");
|
|
return;
|
|
}
|
|
var inverted = params.commandName[0] === "v";
|
|
if (argString[0] === "!" && params.commandName[0] === "g") {
|
|
inverted = true;
|
|
argString = argString.slice(1);
|
|
}
|
|
var lineStart = params.line !== void 0 ? params.line : cm.firstLine();
|
|
var lineEnd = params.lineEnd || params.line || cm.lastLine();
|
|
var tokens = splitBySlash(argString);
|
|
var regexPart = argString, cmd = "";
|
|
if (tokens && tokens.length) {
|
|
regexPart = tokens[0];
|
|
cmd = tokens.slice(1, tokens.length).join("/");
|
|
}
|
|
if (regexPart) {
|
|
try {
|
|
updateSearchQuery(
|
|
cm,
|
|
regexPart,
|
|
true,
|
|
true
|
|
/** smartCase */
|
|
);
|
|
} catch (e) {
|
|
showConfirm(cm, "Invalid regex: " + regexPart);
|
|
return;
|
|
}
|
|
}
|
|
var query = getSearchState(cm).getQuery();
|
|
var matchedLines = [];
|
|
for (var i = lineStart; i <= lineEnd; i++) {
|
|
var line = cm.getLine(i);
|
|
var matched = query.test(line);
|
|
if (matched !== inverted) {
|
|
matchedLines.push(cmd ? cm.getLineHandle(i) : line);
|
|
}
|
|
}
|
|
if (!cmd) {
|
|
showConfirm(cm, matchedLines.join("\n"));
|
|
return;
|
|
}
|
|
var index = 0;
|
|
var nextCommand = function() {
|
|
if (index < matchedLines.length) {
|
|
var lineHandle = matchedLines[index++];
|
|
var lineNum = cm.getLineNumber(lineHandle);
|
|
if (lineNum == null) {
|
|
nextCommand();
|
|
return;
|
|
}
|
|
var command = lineNum + 1 + cmd;
|
|
exCommandDispatcher.processCommand(cm, command, {
|
|
callback: nextCommand
|
|
});
|
|
} else if (cm.releaseLineHandles) {
|
|
cm.releaseLineHandles();
|
|
}
|
|
};
|
|
nextCommand();
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
substitute: function(cm, params) {
|
|
if (!cm.getSearchCursor) {
|
|
throw new Error("Search feature not available. Requires searchcursor.js or any other getSearchCursor implementation.");
|
|
}
|
|
var argString = params.argString;
|
|
var tokens = argString ? splitBySeparator(argString, argString[0]) : [];
|
|
var regexPart = "", replacePart = "", trailing, flagsPart, count;
|
|
var confirm = false;
|
|
var global = false;
|
|
if (tokens && tokens.length) {
|
|
regexPart = tokens[0];
|
|
if (getOption("pcre") && regexPart !== "") {
|
|
regexPart = new RegExp(regexPart).source;
|
|
}
|
|
replacePart = tokens[1];
|
|
if (replacePart !== void 0) {
|
|
if (getOption("pcre")) {
|
|
replacePart = unescapeRegexReplace(replacePart.replace(/([^\\])&/g, "$1$$&"));
|
|
} else {
|
|
replacePart = translateRegexReplace(replacePart);
|
|
}
|
|
vimGlobalState.lastSubstituteReplacePart = replacePart;
|
|
}
|
|
trailing = tokens[2] ? tokens[2].split(" ") : [];
|
|
} else {
|
|
if (argString && argString.length) {
|
|
showConfirm(cm, "Substitutions should be of the form :s/pattern/replace/");
|
|
return;
|
|
}
|
|
}
|
|
if (trailing) {
|
|
flagsPart = trailing[0];
|
|
count = parseInt(trailing[1]);
|
|
if (flagsPart) {
|
|
if (flagsPart.indexOf("c") != -1) {
|
|
confirm = true;
|
|
}
|
|
if (flagsPart.indexOf("g") != -1) {
|
|
global = true;
|
|
}
|
|
if (getOption("pcre")) {
|
|
regexPart = regexPart + "/" + flagsPart;
|
|
} else {
|
|
regexPart = regexPart.replace(/\//g, "\\/") + "/" + flagsPart;
|
|
}
|
|
}
|
|
}
|
|
if (regexPart) {
|
|
try {
|
|
updateSearchQuery(
|
|
cm,
|
|
regexPart,
|
|
true,
|
|
true
|
|
/** smartCase */
|
|
);
|
|
} catch (e) {
|
|
showConfirm(cm, "Invalid regex: " + regexPart);
|
|
return;
|
|
}
|
|
}
|
|
replacePart = replacePart || vimGlobalState.lastSubstituteReplacePart;
|
|
if (replacePart === void 0) {
|
|
showConfirm(cm, "No previous substitute regular expression");
|
|
return;
|
|
}
|
|
var state = getSearchState(cm);
|
|
var query = state.getQuery();
|
|
var lineStart = params.line !== void 0 ? params.line : cm.getCursor().line;
|
|
var lineEnd = params.lineEnd || lineStart;
|
|
if (lineStart == cm.firstLine() && lineEnd == cm.lastLine()) {
|
|
lineEnd = Infinity;
|
|
}
|
|
if (count) {
|
|
lineStart = lineEnd;
|
|
lineEnd = lineStart + count - 1;
|
|
}
|
|
var startPos = clipCursorToContent(cm, new Pos2(lineStart, 0));
|
|
var cursor = cm.getSearchCursor(query, startPos);
|
|
doReplace(cm, confirm, global, lineStart, lineEnd, cursor, query, replacePart, params.callback);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
startinsert: function(cm, params) {
|
|
doKeyToKey(cm, params.argString == "!" ? "A" : "i", {});
|
|
},
|
|
redo: CM.commands.redo,
|
|
undo: CM.commands.undo,
|
|
/** @arg {CodeMirrorV} cm */
|
|
write: function(cm) {
|
|
if (CM.commands.save) {
|
|
CM.commands.save(cm);
|
|
} else if (cm.save) {
|
|
cm.save();
|
|
}
|
|
},
|
|
/** @arg {CodeMirrorV} cm */
|
|
nohlsearch: function(cm) {
|
|
clearSearchHighlight(cm);
|
|
},
|
|
/** @arg {CodeMirrorV} cm */
|
|
yank: function(cm) {
|
|
var cur2 = copyCursor(cm.getCursor());
|
|
var line = cur2.line;
|
|
var lineText = cm.getLine(line);
|
|
vimGlobalState.registerController.pushText(
|
|
"0",
|
|
"yank",
|
|
lineText,
|
|
true,
|
|
true
|
|
);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
delete: function(cm, params) {
|
|
var line = params.selectionLine;
|
|
var lineEnd = isNaN(params.selectionLineEnd) ? line : params.selectionLineEnd;
|
|
operators.delete(cm, { linewise: true }, [
|
|
{
|
|
anchor: new Pos2(line, 0),
|
|
head: new Pos2(lineEnd + 1, 0)
|
|
}
|
|
]);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
join: function(cm, params) {
|
|
var line = params.selectionLine;
|
|
var lineEnd = isNaN(params.selectionLineEnd) ? line : params.selectionLineEnd;
|
|
cm.setCursor(new Pos2(line, 0));
|
|
actions.joinLines(cm, { repeat: lineEnd - line }, cm.state.vim);
|
|
},
|
|
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
|
|
delmarks: function(cm, params) {
|
|
if (!params.argString || !trim(params.argString)) {
|
|
showConfirm(cm, "Argument required");
|
|
return;
|
|
}
|
|
var state = cm.state.vim;
|
|
var stream = new CM.StringStream(trim(params.argString));
|
|
while (!stream.eol()) {
|
|
stream.eatSpace();
|
|
var count = stream.pos;
|
|
if (!stream.match(/[a-zA-Z]/, false)) {
|
|
showConfirm(cm, "Invalid argument: " + params.argString.substring(count));
|
|
return;
|
|
}
|
|
var sym = stream.next();
|
|
if (stream.match("-", true)) {
|
|
if (!stream.match(/[a-zA-Z]/, false)) {
|
|
showConfirm(cm, "Invalid argument: " + params.argString.substring(count));
|
|
return;
|
|
}
|
|
var startMark = sym;
|
|
var finishMark = stream.next();
|
|
if (startMark && finishMark && isLowerCase(startMark) == isLowerCase(finishMark)) {
|
|
var start = startMark.charCodeAt(0);
|
|
var finish = finishMark.charCodeAt(0);
|
|
if (start >= finish) {
|
|
showConfirm(cm, "Invalid argument: " + params.argString.substring(count));
|
|
return;
|
|
}
|
|
for (var j = 0; j <= finish - start; j++) {
|
|
var mark = String.fromCharCode(start + j);
|
|
delete state.marks[mark];
|
|
}
|
|
} else {
|
|
showConfirm(cm, "Invalid argument: " + startMark + "-");
|
|
return;
|
|
}
|
|
} else if (sym) {
|
|
delete state.marks[sym];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var exCommandDispatcher = new ExCommandDispatcher();
|
|
vimApi.defineEx("version", "ve", (cm) => {
|
|
showConfirm(cm, "Codemirror-vim version: 6.3.0");
|
|
});
|
|
function doReplace(cm, confirm, global, lineStart, lineEnd, searchCursor, query, replaceWith, callback) {
|
|
cm.state.vim.exMode = true;
|
|
var done = false;
|
|
var matches = 0;
|
|
var lastPos;
|
|
var modifiedLineNumber;
|
|
var joined;
|
|
function replaceAll() {
|
|
cm.operation(function() {
|
|
while (!done) {
|
|
replace();
|
|
next();
|
|
}
|
|
stop();
|
|
});
|
|
}
|
|
function replace() {
|
|
var newText = "";
|
|
var match = searchCursor.match || searchCursor.pos && searchCursor.pos.match;
|
|
if (match) {
|
|
newText = replaceWith.replace(/\$(\d{1,3}|[$&])/g, function(_, x) {
|
|
if (x == "$") return "$";
|
|
if (x == "&") return match[0];
|
|
var x1 = x;
|
|
while (parseInt(x1) >= match.length && x1.length > 0) {
|
|
x1 = x1.slice(0, x1.length - 1);
|
|
}
|
|
if (x1) return match[x1] + x.slice(x1.length, x.length);
|
|
return _;
|
|
});
|
|
} else {
|
|
var text = cm.getRange(searchCursor.from(), searchCursor.to());
|
|
newText = text.replace(query, replaceWith);
|
|
}
|
|
var unmodifiedLineNumber = searchCursor.to().line;
|
|
searchCursor.replace(newText);
|
|
modifiedLineNumber = searchCursor.to().line;
|
|
lineEnd += modifiedLineNumber - unmodifiedLineNumber;
|
|
joined = modifiedLineNumber < unmodifiedLineNumber;
|
|
}
|
|
function findNextValidMatch() {
|
|
var lastMatchTo = lastPos && copyCursor(searchCursor.to());
|
|
var match = searchCursor.findNext();
|
|
if (match && !match[0] && lastMatchTo && cursorEqual(searchCursor.from(), lastMatchTo)) {
|
|
match = searchCursor.findNext();
|
|
}
|
|
if (match) matches++;
|
|
return match;
|
|
}
|
|
function next() {
|
|
while (findNextValidMatch() && isInRange(searchCursor.from(), lineStart, lineEnd)) {
|
|
if (!global && searchCursor.from().line == modifiedLineNumber && !joined) {
|
|
continue;
|
|
}
|
|
cm.scrollIntoView(searchCursor.from(), 30);
|
|
cm.setSelection(searchCursor.from(), searchCursor.to());
|
|
lastPos = searchCursor.from();
|
|
done = false;
|
|
return;
|
|
}
|
|
done = true;
|
|
}
|
|
function stop(close) {
|
|
if (close) {
|
|
close();
|
|
}
|
|
cm.focus();
|
|
if (lastPos) {
|
|
cm.setCursor(lastPos);
|
|
var vim2 = cm.state.vim;
|
|
vim2.exMode = false;
|
|
vim2.lastHPos = vim2.lastHSPos = lastPos.ch;
|
|
}
|
|
if (callback) {
|
|
callback();
|
|
} else if (done) {
|
|
showConfirm(
|
|
cm,
|
|
(matches ? "Found " + matches + " matches" : "No matches found") + " for pattern: " + query + (getOption("pcre") ? " (set nopcre to use Vim regexps)" : "")
|
|
);
|
|
}
|
|
}
|
|
function onPromptKeyDown(e, _value, close) {
|
|
CM.e_stop(e);
|
|
var keyName = vimKeyFromEvent(e);
|
|
switch (keyName) {
|
|
case "y":
|
|
replace();
|
|
next();
|
|
break;
|
|
case "n":
|
|
next();
|
|
break;
|
|
case "a":
|
|
var savedCallback = callback;
|
|
callback = void 0;
|
|
cm.operation(replaceAll);
|
|
callback = savedCallback;
|
|
break;
|
|
case "l":
|
|
replace();
|
|
// fall through and exit.
|
|
case "q":
|
|
case "<Esc>":
|
|
case "<C-c>":
|
|
case "<C-[>":
|
|
stop(close);
|
|
break;
|
|
}
|
|
if (done) {
|
|
stop(close);
|
|
}
|
|
return true;
|
|
}
|
|
next();
|
|
if (done) {
|
|
showConfirm(cm, "No matches for " + query + (getOption("pcre") ? " (set nopcre to use vim regexps)" : ""));
|
|
return;
|
|
}
|
|
if (!confirm) {
|
|
replaceAll();
|
|
if (callback) {
|
|
callback();
|
|
}
|
|
return;
|
|
}
|
|
showPrompt(cm, {
|
|
prefix: dom("span", "replace with ", dom("strong", replaceWith), " (y/n/a/q/l)"),
|
|
onKeyDown: onPromptKeyDown
|
|
});
|
|
}
|
|
function exitInsertMode(cm, keepCursor) {
|
|
var vim2 = cm.state.vim;
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
var insertModeChangeRegister = vimGlobalState.registerController.getRegister(".");
|
|
var isPlaying = macroModeState.isPlaying;
|
|
var lastChange = macroModeState.lastInsertModeChanges;
|
|
if (!isPlaying) {
|
|
cm.off("change", onChange);
|
|
if (vim2.insertEnd) vim2.insertEnd.clear();
|
|
vim2.insertEnd = void 0;
|
|
CM.off(cm.getInputField(), "keydown", onKeyEventTargetKeyDown);
|
|
}
|
|
if (!isPlaying && vim2.insertModeRepeat && vim2.insertModeRepeat > 1) {
|
|
repeatLastEdit(
|
|
cm,
|
|
vim2,
|
|
vim2.insertModeRepeat - 1,
|
|
true
|
|
/** repeatForInsert */
|
|
);
|
|
vim2.lastEditInputState.repeatOverride = vim2.insertModeRepeat;
|
|
}
|
|
delete vim2.insertModeRepeat;
|
|
vim2.insertMode = false;
|
|
if (!keepCursor) {
|
|
cm.setCursor(cm.getCursor().line, cm.getCursor().ch - 1);
|
|
}
|
|
cm.setOption("keyMap", "vim");
|
|
cm.setOption("disableInput", true);
|
|
cm.toggleOverwrite(false);
|
|
insertModeChangeRegister.setText(lastChange.changes.join(""));
|
|
CM.signal(cm, "vim-mode-change", { mode: "normal" });
|
|
if (macroModeState.isRecording) {
|
|
logInsertModeChange(macroModeState);
|
|
}
|
|
}
|
|
function _mapCommand(command) {
|
|
defaultKeymap2.unshift(command);
|
|
if (command.keys) addUsedKeys(command.keys);
|
|
}
|
|
function addUsedKeys(keys2) {
|
|
keys2.split(/(<(?:[CSMA]-)*\w+>|.)/i).forEach(function(part) {
|
|
if (part) {
|
|
if (!usedKeys[part]) usedKeys[part] = 0;
|
|
usedKeys[part]++;
|
|
}
|
|
});
|
|
}
|
|
function removeUsedKeys(keys2) {
|
|
keys2.split(/(<(?:[CSMA]-)*\w+>|.)/i).forEach(function(part) {
|
|
if (usedKeys[part])
|
|
usedKeys[part]--;
|
|
});
|
|
}
|
|
function mapCommand(keys2, type, name, args, extra) {
|
|
var command = { keys: keys2, type };
|
|
command[type] = name;
|
|
command[type + "Args"] = args;
|
|
for (var key in extra)
|
|
command[key] = extra[key];
|
|
_mapCommand(command);
|
|
}
|
|
defineOption("insertModeEscKeysTimeout", 200, "number");
|
|
function executeMacroRegister(cm, vim2, macroModeState, registerName) {
|
|
var register = vimGlobalState.registerController.getRegister(registerName);
|
|
if (registerName == ":") {
|
|
if (register.keyBuffer[0]) {
|
|
exCommandDispatcher.processCommand(cm, register.keyBuffer[0]);
|
|
}
|
|
macroModeState.isPlaying = false;
|
|
return;
|
|
}
|
|
var keyBuffer = register.keyBuffer;
|
|
var imc = 0;
|
|
macroModeState.isPlaying = true;
|
|
macroModeState.replaySearchQueries = register.searchQueries.slice(0);
|
|
for (var i = 0; i < keyBuffer.length; i++) {
|
|
var text = keyBuffer[i];
|
|
var match, key;
|
|
var keyRe = /<(?:[CSMA]-)*\w+>|./gi;
|
|
while (match = keyRe.exec(text)) {
|
|
key = match[0];
|
|
vimApi.handleKey(cm, key, "macro");
|
|
if (vim2.insertMode) {
|
|
var changes = register.insertModeChanges[imc++].changes;
|
|
vimGlobalState.macroModeState.lastInsertModeChanges.changes = changes;
|
|
repeatInsertModeChanges(cm, changes, 1);
|
|
exitInsertMode(cm);
|
|
}
|
|
}
|
|
}
|
|
macroModeState.isPlaying = false;
|
|
}
|
|
function logKey(macroModeState, key) {
|
|
if (macroModeState.isPlaying) {
|
|
return;
|
|
}
|
|
var registerName = macroModeState.latestRegister;
|
|
var register = vimGlobalState.registerController.getRegister(registerName);
|
|
if (register) {
|
|
register.pushText(key);
|
|
}
|
|
}
|
|
function logInsertModeChange(macroModeState) {
|
|
if (macroModeState.isPlaying) {
|
|
return;
|
|
}
|
|
var registerName = macroModeState.latestRegister;
|
|
var register = vimGlobalState.registerController.getRegister(registerName);
|
|
if (register && register.pushInsertModeChanges) {
|
|
register.pushInsertModeChanges(macroModeState.lastInsertModeChanges);
|
|
}
|
|
}
|
|
function logSearchQuery(macroModeState, query) {
|
|
if (macroModeState.isPlaying) {
|
|
return;
|
|
}
|
|
var registerName = macroModeState.latestRegister;
|
|
var register = vimGlobalState.registerController.getRegister(registerName);
|
|
if (register && register.pushSearchQuery) {
|
|
register.pushSearchQuery(query);
|
|
}
|
|
}
|
|
function onChange(cm, changeObj) {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
var lastChange = macroModeState.lastInsertModeChanges;
|
|
if (!macroModeState.isPlaying) {
|
|
var vim2 = cm.state.vim;
|
|
while (changeObj) {
|
|
lastChange.expectCursorActivityForChange = true;
|
|
if (lastChange.ignoreCount > 1) {
|
|
lastChange.ignoreCount--;
|
|
} else if (changeObj.origin == "+input" || changeObj.origin == "paste" || changeObj.origin === void 0) {
|
|
var selectionCount = cm.listSelections().length;
|
|
if (selectionCount > 1)
|
|
lastChange.ignoreCount = selectionCount;
|
|
var text = changeObj.text.join("\n");
|
|
if (lastChange.maybeReset) {
|
|
lastChange.changes = [];
|
|
lastChange.maybeReset = false;
|
|
}
|
|
if (text) {
|
|
if (cm.state.overwrite && !/\n/.test(text)) {
|
|
lastChange.changes.push([text]);
|
|
} else {
|
|
if (text.length > 1) {
|
|
var insertEnd = vim2 && vim2.insertEnd && vim2.insertEnd.find();
|
|
var cursor = cm.getCursor();
|
|
if (insertEnd && insertEnd.line == cursor.line) {
|
|
var offset = insertEnd.ch - cursor.ch;
|
|
if (offset > 0 && offset < text.length) {
|
|
lastChange.changes.push([text, offset]);
|
|
text = "";
|
|
}
|
|
}
|
|
}
|
|
if (text) lastChange.changes.push(text);
|
|
}
|
|
}
|
|
}
|
|
changeObj = changeObj.next;
|
|
}
|
|
}
|
|
}
|
|
function onCursorActivity(cm) {
|
|
var _a;
|
|
var vim2 = cm.state.vim;
|
|
if (vim2.insertMode) {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
if (macroModeState.isPlaying) {
|
|
return;
|
|
}
|
|
var lastChange = macroModeState.lastInsertModeChanges;
|
|
if (lastChange.expectCursorActivityForChange) {
|
|
lastChange.expectCursorActivityForChange = false;
|
|
} else {
|
|
lastChange.maybeReset = true;
|
|
if (vim2.insertEnd) vim2.insertEnd.clear();
|
|
vim2.insertEnd = cm.setBookmark(cm.getCursor(), { insertLeft: true });
|
|
}
|
|
} else if (!((_a = cm.curOp) == null ? void 0 : _a.isVimOp)) {
|
|
handleExternalSelection(cm, vim2);
|
|
}
|
|
}
|
|
function handleExternalSelection(cm, vim2) {
|
|
var anchor = cm.getCursor("anchor");
|
|
var head = cm.getCursor("head");
|
|
if (vim2.visualMode && !cm.somethingSelected()) {
|
|
exitVisualMode(cm, false);
|
|
} else if (!vim2.visualMode && !vim2.insertMode && cm.somethingSelected()) {
|
|
vim2.visualMode = true;
|
|
vim2.visualLine = false;
|
|
CM.signal(cm, "vim-mode-change", { mode: "visual" });
|
|
}
|
|
if (vim2.visualMode) {
|
|
var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0;
|
|
var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0;
|
|
head = offsetCursor(head, 0, headOffset);
|
|
anchor = offsetCursor(anchor, 0, anchorOffset);
|
|
vim2.sel = {
|
|
anchor,
|
|
head
|
|
};
|
|
updateMark(cm, vim2, "<", cursorMin(head, anchor));
|
|
updateMark(cm, vim2, ">", cursorMax(head, anchor));
|
|
} else if (!vim2.insertMode) {
|
|
vim2.lastHPos = cm.getCursor().ch;
|
|
}
|
|
}
|
|
function onKeyEventTargetKeyDown(e) {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
var lastChange = macroModeState.lastInsertModeChanges;
|
|
var keyName = CM.keyName ? CM.keyName(e) : e.key;
|
|
if (!keyName) {
|
|
return;
|
|
}
|
|
if (keyName.indexOf("Delete") != -1 || keyName.indexOf("Backspace") != -1) {
|
|
if (lastChange.maybeReset) {
|
|
lastChange.changes = [];
|
|
lastChange.maybeReset = false;
|
|
}
|
|
lastChange.changes.push(new InsertModeKey(keyName, e));
|
|
}
|
|
}
|
|
function repeatLastEdit(cm, vim2, repeat, repeatForInsert) {
|
|
var macroModeState = vimGlobalState.macroModeState;
|
|
macroModeState.isPlaying = true;
|
|
var lastAction = vim2.lastEditActionCommand;
|
|
var cachedInputState = vim2.inputState;
|
|
function repeatCommand() {
|
|
if (lastAction) {
|
|
commandDispatcher.processAction(cm, vim2, lastAction);
|
|
} else {
|
|
commandDispatcher.evalInput(cm, vim2);
|
|
}
|
|
}
|
|
function repeatInsert(repeat2) {
|
|
if (macroModeState.lastInsertModeChanges.changes.length > 0) {
|
|
repeat2 = !vim2.lastEditActionCommand ? 1 : repeat2;
|
|
var changeObject = macroModeState.lastInsertModeChanges;
|
|
repeatInsertModeChanges(cm, changeObject.changes, repeat2);
|
|
}
|
|
}
|
|
vim2.inputState = vim2.lastEditInputState;
|
|
if (lastAction && lastAction.interlaceInsertRepeat) {
|
|
for (var i = 0; i < repeat; i++) {
|
|
repeatCommand();
|
|
repeatInsert(1);
|
|
}
|
|
} else {
|
|
if (!repeatForInsert) {
|
|
repeatCommand();
|
|
}
|
|
repeatInsert(repeat);
|
|
}
|
|
vim2.inputState = cachedInputState;
|
|
if (vim2.insertMode && !repeatForInsert) {
|
|
exitInsertMode(cm);
|
|
}
|
|
macroModeState.isPlaying = false;
|
|
}
|
|
function sendCmKey(cm, key) {
|
|
CM.lookupKey(key, "vim-insert", function keyHandler(binding) {
|
|
if (typeof binding == "string") {
|
|
CM.commands[binding](cm);
|
|
} else {
|
|
binding(cm);
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
function repeatInsertModeChanges(cm, changes, repeat) {
|
|
var head = cm.getCursor("head");
|
|
var visualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.visualBlock;
|
|
if (visualBlock) {
|
|
selectForInsert(cm, head, visualBlock + 1);
|
|
repeat = cm.listSelections().length;
|
|
cm.setCursor(head);
|
|
}
|
|
for (var i = 0; i < repeat; i++) {
|
|
if (visualBlock) {
|
|
cm.setCursor(offsetCursor(head, i, 0));
|
|
}
|
|
for (var j = 0; j < changes.length; j++) {
|
|
var change = changes[j];
|
|
if (change instanceof InsertModeKey) {
|
|
sendCmKey(cm, change.keyName);
|
|
} else if (typeof change == "string") {
|
|
cm.replaceSelection(change);
|
|
} else {
|
|
var start = cm.getCursor();
|
|
var end = offsetCursor(start, 0, change[0].length - (change[1] || 0));
|
|
cm.replaceRange(change[0], start, change[1] ? start : end);
|
|
cm.setCursor(end);
|
|
}
|
|
}
|
|
}
|
|
if (visualBlock) {
|
|
cm.setCursor(offsetCursor(head, 0, 1));
|
|
}
|
|
}
|
|
function cloneVimState(state) {
|
|
var n = new state.constructor();
|
|
Object.keys(state).forEach(function(key) {
|
|
if (key == "insertEnd") return;
|
|
var o = state[key];
|
|
if (Array.isArray(o))
|
|
o = o.slice();
|
|
else if (o && typeof o == "object" && o.constructor != Object)
|
|
o = cloneVimState(o);
|
|
n[key] = o;
|
|
});
|
|
if (state.sel) {
|
|
n.sel = {
|
|
head: state.sel.head && copyCursor(state.sel.head),
|
|
anchor: state.sel.anchor && copyCursor(state.sel.anchor)
|
|
};
|
|
}
|
|
return n;
|
|
}
|
|
function multiSelectHandleKey(cm_, key, origin) {
|
|
var vim2 = maybeInitVimState(cm_);
|
|
var cm = (
|
|
/**@type {CodeMirrorV}*/
|
|
cm_
|
|
);
|
|
var isHandled = false;
|
|
var vim2 = vimApi.maybeInitVimState_(cm);
|
|
var visualBlock = vim2.visualBlock || vim2.wasInVisualBlock;
|
|
if (cm.state.closeVimNotification) {
|
|
var close = cm.state.closeVimNotification;
|
|
cm.state.closeVimNotification = null;
|
|
close();
|
|
if (key == "<CR>") {
|
|
clearInputState(cm);
|
|
return true;
|
|
}
|
|
}
|
|
var wasMultiselect = cm.isInMultiSelectMode();
|
|
if (vim2.wasInVisualBlock && !wasMultiselect) {
|
|
vim2.wasInVisualBlock = false;
|
|
} else if (wasMultiselect && vim2.visualBlock) {
|
|
vim2.wasInVisualBlock = true;
|
|
}
|
|
if (key == "<Esc>" && !vim2.insertMode && !vim2.visualMode && wasMultiselect && vim2.status == "<Esc>") {
|
|
clearInputState(cm);
|
|
} else if (visualBlock || !wasMultiselect || cm.inVirtualSelectionMode) {
|
|
isHandled = vimApi.handleKey(cm, key, origin);
|
|
} else {
|
|
var old = cloneVimState(vim2);
|
|
var changeQueueList = vim2.inputState.changeQueueList || [];
|
|
cm.operation(function() {
|
|
var _a;
|
|
if (cm.curOp)
|
|
cm.curOp.isVimOp = true;
|
|
var index = 0;
|
|
cm.forEachSelection(function() {
|
|
cm.state.vim.inputState.changeQueue = changeQueueList[index];
|
|
var head = cm.getCursor("head");
|
|
var anchor = cm.getCursor("anchor");
|
|
var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0;
|
|
var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0;
|
|
head = offsetCursor(head, 0, headOffset);
|
|
anchor = offsetCursor(anchor, 0, anchorOffset);
|
|
cm.state.vim.sel.head = head;
|
|
cm.state.vim.sel.anchor = anchor;
|
|
isHandled = vimApi.handleKey(cm, key, origin);
|
|
if (cm.virtualSelection) {
|
|
changeQueueList[index] = cm.state.vim.inputState.changeQueue;
|
|
cm.state.vim = cloneVimState(old);
|
|
}
|
|
index++;
|
|
});
|
|
if (((_a = cm.curOp) == null ? void 0 : _a.cursorActivity) && !isHandled)
|
|
cm.curOp.cursorActivity = false;
|
|
cm.state.vim = vim2;
|
|
vim2.inputState.changeQueueList = changeQueueList;
|
|
vim2.inputState.changeQueue = null;
|
|
}, true);
|
|
}
|
|
if (isHandled && !vim2.visualMode && !vim2.insertMode && vim2.visualMode != cm.somethingSelected()) {
|
|
handleExternalSelection(cm, vim2);
|
|
}
|
|
return isHandled;
|
|
}
|
|
resetVimGlobalState();
|
|
return vimApi;
|
|
}
|
|
function indexFromPos(doc, pos) {
|
|
var ch = pos.ch;
|
|
var lineNumber = pos.line + 1;
|
|
if (lineNumber < 1) {
|
|
lineNumber = 1;
|
|
ch = 0;
|
|
}
|
|
if (lineNumber > doc.lines) {
|
|
lineNumber = doc.lines;
|
|
ch = Number.MAX_VALUE;
|
|
}
|
|
var line = doc.line(lineNumber);
|
|
return Math.min(line.from + Math.max(0, ch), line.to);
|
|
}
|
|
function posFromIndex(doc, offset) {
|
|
let line = doc.lineAt(offset);
|
|
return { line: line.number - 1, ch: offset - line.from };
|
|
}
|
|
var Pos = class {
|
|
constructor(line, ch) {
|
|
this.line = line;
|
|
this.ch = ch;
|
|
}
|
|
};
|
|
function on(emitter, type, f) {
|
|
if (emitter.addEventListener) {
|
|
emitter.addEventListener(type, f, false);
|
|
} else {
|
|
var map = emitter._handlers || (emitter._handlers = {});
|
|
map[type] = (map[type] || []).concat(f);
|
|
}
|
|
}
|
|
function off(emitter, type, f) {
|
|
if (emitter.removeEventListener) {
|
|
emitter.removeEventListener(type, f, false);
|
|
} else {
|
|
var map = emitter._handlers, arr = map && map[type];
|
|
if (arr) {
|
|
var index = arr.indexOf(f);
|
|
if (index > -1) {
|
|
map[type] = arr.slice(0, index).concat(arr.slice(index + 1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function signal(emitter, type, ...args) {
|
|
var _a;
|
|
var handlers = (_a = emitter._handlers) === null || _a === void 0 ? void 0 : _a[type];
|
|
if (!handlers)
|
|
return;
|
|
for (var i = 0; i < handlers.length; ++i) {
|
|
handlers[i](...args);
|
|
}
|
|
}
|
|
function signalTo(handlers, ...args) {
|
|
if (!handlers)
|
|
return;
|
|
for (var i = 0; i < handlers.length; ++i) {
|
|
handlers[i](...args);
|
|
}
|
|
}
|
|
var wordChar;
|
|
try {
|
|
wordChar = /* @__PURE__ */ new RegExp("[\\w\\p{Alphabetic}\\p{Number}_]", "u");
|
|
} catch (_) {
|
|
wordChar = /[\w]/;
|
|
}
|
|
function dispatchChange(cm, transaction) {
|
|
var view = cm.cm6;
|
|
if (view.state.readOnly)
|
|
return;
|
|
var type = "input.type.compose";
|
|
if (cm.curOp) {
|
|
if (!cm.curOp.lastChange)
|
|
type = "input.type.compose.start";
|
|
}
|
|
if (transaction.annotations) {
|
|
try {
|
|
transaction.annotations.some(function(note) {
|
|
if (note.value == "input")
|
|
note.value = type;
|
|
});
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
} else {
|
|
transaction.userEvent = type;
|
|
}
|
|
return view.dispatch(transaction);
|
|
}
|
|
function runHistoryCommand(cm, revert) {
|
|
var _a;
|
|
if (cm.curOp) {
|
|
cm.curOp.$changeStart = void 0;
|
|
}
|
|
(revert ? import_commands.undo : import_commands.redo)(cm.cm6);
|
|
let changeStartIndex = (_a = cm.curOp) === null || _a === void 0 ? void 0 : _a.$changeStart;
|
|
if (changeStartIndex != null) {
|
|
cm.cm6.dispatch({ selection: { anchor: changeStartIndex } });
|
|
}
|
|
}
|
|
var keys = {
|
|
Left: (cm) => (0, import_view.runScopeHandlers)(cm.cm6, { key: "Left" }, "editor"),
|
|
Right: (cm) => (0, import_view.runScopeHandlers)(cm.cm6, { key: "Right" }, "editor"),
|
|
Up: (cm) => (0, import_view.runScopeHandlers)(cm.cm6, { key: "Up" }, "editor"),
|
|
Down: (cm) => (0, import_view.runScopeHandlers)(cm.cm6, { key: "Down" }, "editor"),
|
|
Backspace: (cm) => (0, import_view.runScopeHandlers)(cm.cm6, { key: "Backspace" }, "editor"),
|
|
Delete: (cm) => (0, import_view.runScopeHandlers)(cm.cm6, { key: "Delete" }, "editor")
|
|
};
|
|
var CodeMirror = class _CodeMirror {
|
|
// --------------------------
|
|
openDialog(template, callback, options) {
|
|
return openDialog(this, template, callback, options);
|
|
}
|
|
openNotification(template, options) {
|
|
return openNotification(this, template, options);
|
|
}
|
|
constructor(cm6) {
|
|
this.state = {};
|
|
this.marks = /* @__PURE__ */ Object.create(null);
|
|
this.$mid = 0;
|
|
this.options = {};
|
|
this._handlers = {};
|
|
this.$lastChangeEndOffset = 0;
|
|
this.virtualSelection = null;
|
|
this.cm6 = cm6;
|
|
this.onChange = this.onChange.bind(this);
|
|
this.onSelectionChange = this.onSelectionChange.bind(this);
|
|
}
|
|
on(type, f) {
|
|
on(this, type, f);
|
|
}
|
|
off(type, f) {
|
|
off(this, type, f);
|
|
}
|
|
signal(type, e, handlers) {
|
|
signal(this, type, e, handlers);
|
|
}
|
|
indexFromPos(pos) {
|
|
return indexFromPos(this.cm6.state.doc, pos);
|
|
}
|
|
posFromIndex(offset) {
|
|
return posFromIndex(this.cm6.state.doc, offset);
|
|
}
|
|
foldCode(pos) {
|
|
let view = this.cm6;
|
|
let ranges = view.state.selection.ranges;
|
|
let doc = this.cm6.state.doc;
|
|
let index = indexFromPos(doc, pos);
|
|
let tmpRanges = import_state.EditorSelection.create([import_state.EditorSelection.range(index, index)], 0).ranges;
|
|
view.state.selection.ranges = tmpRanges;
|
|
(0, import_language.foldCode)(view);
|
|
view.state.selection.ranges = ranges;
|
|
}
|
|
firstLine() {
|
|
return 0;
|
|
}
|
|
lastLine() {
|
|
return this.cm6.state.doc.lines - 1;
|
|
}
|
|
lineCount() {
|
|
return this.cm6.state.doc.lines;
|
|
}
|
|
setCursor(line, ch) {
|
|
if (typeof line === "object") {
|
|
ch = line.ch;
|
|
line = line.line;
|
|
}
|
|
var offset = indexFromPos(this.cm6.state.doc, { line, ch: ch || 0 });
|
|
this.cm6.dispatch({ selection: { anchor: offset } }, { scrollIntoView: !this.curOp });
|
|
if (this.curOp && !this.curOp.isVimOp)
|
|
this.onBeforeEndOperation();
|
|
}
|
|
getCursor(p) {
|
|
var sel = this.cm6.state.selection.main;
|
|
var offset = p == "head" || !p ? sel.head : p == "anchor" ? sel.anchor : p == "start" ? sel.from : p == "end" ? sel.to : null;
|
|
if (offset == null)
|
|
throw new Error("Invalid cursor type");
|
|
return this.posFromIndex(offset);
|
|
}
|
|
listSelections() {
|
|
var doc = this.cm6.state.doc;
|
|
return this.cm6.state.selection.ranges.map((r) => {
|
|
return {
|
|
anchor: posFromIndex(doc, r.anchor),
|
|
head: posFromIndex(doc, r.head)
|
|
};
|
|
});
|
|
}
|
|
setSelections(p, primIndex) {
|
|
var doc = this.cm6.state.doc;
|
|
var ranges = p.map((x) => {
|
|
var head = indexFromPos(doc, x.head);
|
|
var anchor = indexFromPos(doc, x.anchor);
|
|
if (head == anchor)
|
|
return import_state.EditorSelection.cursor(head, 1);
|
|
return import_state.EditorSelection.range(anchor, head);
|
|
});
|
|
this.cm6.dispatch({
|
|
selection: import_state.EditorSelection.create(ranges, primIndex)
|
|
});
|
|
}
|
|
setSelection(anchor, head, options) {
|
|
this.setSelections([{ anchor, head }], 0);
|
|
if (options && options.origin == "*mouse") {
|
|
this.onBeforeEndOperation();
|
|
}
|
|
}
|
|
getLine(row) {
|
|
var doc = this.cm6.state.doc;
|
|
if (row < 0 || row >= doc.lines)
|
|
return "";
|
|
return this.cm6.state.doc.line(row + 1).text;
|
|
}
|
|
getLineHandle(row) {
|
|
if (!this.$lineHandleChanges)
|
|
this.$lineHandleChanges = [];
|
|
return { row, index: this.indexFromPos(new Pos(row, 0)) };
|
|
}
|
|
getLineNumber(handle) {
|
|
var updates = this.$lineHandleChanges;
|
|
if (!updates)
|
|
return null;
|
|
var offset = handle.index;
|
|
for (var i = 0; i < updates.length; i++) {
|
|
offset = updates[i].changes.mapPos(offset, 1, import_state.MapMode.TrackAfter);
|
|
if (offset == null)
|
|
return null;
|
|
}
|
|
var pos = this.posFromIndex(offset);
|
|
return pos.ch == 0 ? pos.line : null;
|
|
}
|
|
releaseLineHandles() {
|
|
this.$lineHandleChanges = void 0;
|
|
}
|
|
getRange(s, e) {
|
|
var doc = this.cm6.state.doc;
|
|
return this.cm6.state.sliceDoc(indexFromPos(doc, s), indexFromPos(doc, e));
|
|
}
|
|
replaceRange(text, s, e, source) {
|
|
if (!e)
|
|
e = s;
|
|
var doc = this.cm6.state.doc;
|
|
var from = indexFromPos(doc, s);
|
|
var to = indexFromPos(doc, e);
|
|
dispatchChange(this, { changes: { from, to, insert: text } });
|
|
}
|
|
replaceSelection(text) {
|
|
dispatchChange(this, this.cm6.state.replaceSelection(text));
|
|
}
|
|
replaceSelections(replacements) {
|
|
var ranges = this.cm6.state.selection.ranges;
|
|
var changes = ranges.map((r, i) => {
|
|
return { from: r.from, to: r.to, insert: replacements[i] || "" };
|
|
});
|
|
dispatchChange(this, { changes });
|
|
}
|
|
getSelection() {
|
|
return this.getSelections().join("\n");
|
|
}
|
|
getSelections() {
|
|
var cm = this.cm6;
|
|
return cm.state.selection.ranges.map((r) => cm.state.sliceDoc(r.from, r.to));
|
|
}
|
|
somethingSelected() {
|
|
return this.cm6.state.selection.ranges.some((r) => !r.empty);
|
|
}
|
|
getInputField() {
|
|
return this.cm6.contentDOM;
|
|
}
|
|
clipPos(p) {
|
|
var doc = this.cm6.state.doc;
|
|
var ch = p.ch;
|
|
var lineNumber = p.line + 1;
|
|
if (lineNumber < 1) {
|
|
lineNumber = 1;
|
|
ch = 0;
|
|
}
|
|
if (lineNumber > doc.lines) {
|
|
lineNumber = doc.lines;
|
|
ch = Number.MAX_VALUE;
|
|
}
|
|
var line = doc.line(lineNumber);
|
|
ch = Math.min(Math.max(0, ch), line.to - line.from);
|
|
return new Pos(lineNumber - 1, ch);
|
|
}
|
|
getValue() {
|
|
return this.cm6.state.doc.toString();
|
|
}
|
|
setValue(text) {
|
|
var cm = this.cm6;
|
|
return cm.dispatch({
|
|
changes: { from: 0, to: cm.state.doc.length, insert: text },
|
|
selection: import_state.EditorSelection.range(0, 0)
|
|
});
|
|
}
|
|
focus() {
|
|
return this.cm6.focus();
|
|
}
|
|
blur() {
|
|
return this.cm6.contentDOM.blur();
|
|
}
|
|
defaultTextHeight() {
|
|
return this.cm6.defaultLineHeight;
|
|
}
|
|
findMatchingBracket(pos, _options) {
|
|
var state = this.cm6.state;
|
|
var offset = indexFromPos(state.doc, pos);
|
|
var m = (0, import_language.matchBrackets)(state, offset + 1, -1);
|
|
if (m && m.end) {
|
|
return { to: posFromIndex(state.doc, m.end.from) };
|
|
}
|
|
m = (0, import_language.matchBrackets)(state, offset, 1);
|
|
if (m && m.end) {
|
|
return { to: posFromIndex(state.doc, m.end.from) };
|
|
}
|
|
return { to: void 0 };
|
|
}
|
|
scanForBracket(pos, dir, style, config3) {
|
|
return scanForBracket(this, pos, dir, style, config3);
|
|
}
|
|
indentLine(line, more) {
|
|
if (more)
|
|
this.indentMore();
|
|
else
|
|
this.indentLess();
|
|
}
|
|
indentMore() {
|
|
(0, import_commands.indentMore)(this.cm6);
|
|
}
|
|
indentLess() {
|
|
(0, import_commands.indentLess)(this.cm6);
|
|
}
|
|
execCommand(name) {
|
|
if (name == "indentAuto")
|
|
_CodeMirror.commands.indentAuto(this);
|
|
else if (name == "goLineLeft")
|
|
(0, import_commands.cursorLineBoundaryBackward)(this.cm6);
|
|
else if (name == "goLineRight") {
|
|
(0, import_commands.cursorLineBoundaryForward)(this.cm6);
|
|
let state = this.cm6.state;
|
|
let cur2 = state.selection.main.head;
|
|
if (cur2 < state.doc.length && state.sliceDoc(cur2, cur2 + 1) !== "\n") {
|
|
(0, import_commands.cursorCharBackward)(this.cm6);
|
|
}
|
|
} else
|
|
console.log(name + " is not implemented");
|
|
}
|
|
setBookmark(cursor, options) {
|
|
var assoc = (options === null || options === void 0 ? void 0 : options.insertLeft) ? 1 : -1;
|
|
var offset = this.indexFromPos(cursor);
|
|
var bm = new Marker(this, offset, assoc);
|
|
return bm;
|
|
}
|
|
addOverlay({ query }) {
|
|
let cm6Query = new import_search.SearchQuery({
|
|
regexp: true,
|
|
search: query.source,
|
|
caseSensitive: !/i/.test(query.flags)
|
|
});
|
|
if (cm6Query.valid) {
|
|
cm6Query.forVim = true;
|
|
this.cm6Query = cm6Query;
|
|
let effect = import_search.setSearchQuery.of(cm6Query);
|
|
this.cm6.dispatch({ effects: effect });
|
|
return cm6Query;
|
|
}
|
|
}
|
|
removeOverlay(overlay) {
|
|
if (!this.cm6Query)
|
|
return;
|
|
this.cm6Query.forVim = false;
|
|
let effect = import_search.setSearchQuery.of(this.cm6Query);
|
|
this.cm6.dispatch({ effects: effect });
|
|
}
|
|
getSearchCursor(query, pos) {
|
|
var cm = this;
|
|
var last = null;
|
|
var lastCM5Result = null;
|
|
var afterEmptyMatch = false;
|
|
if (pos.ch == void 0)
|
|
pos.ch = Number.MAX_VALUE;
|
|
var firstOffset = indexFromPos(cm.cm6.state.doc, pos);
|
|
var source = query.source.replace(/(\\.|{(?:\d+(?:,\d*)?|,\d+)})|[{}]/g, function(a, b) {
|
|
if (!b)
|
|
return "\\" + a;
|
|
return b;
|
|
});
|
|
function rCursor(doc, from = 0, to = doc.length) {
|
|
return new import_search.RegExpCursor(doc, source, { ignoreCase: query.ignoreCase }, from, to);
|
|
}
|
|
function nextMatch(from) {
|
|
var doc = cm.cm6.state.doc;
|
|
if (from > doc.length)
|
|
return null;
|
|
let res = rCursor(doc, from).next();
|
|
return res.done ? null : res.value;
|
|
}
|
|
var ChunkSize = 1e4;
|
|
function prevMatchInRange(from, to) {
|
|
var doc = cm.cm6.state.doc;
|
|
for (let size = 1; ; size++) {
|
|
let start = Math.max(from, to - size * ChunkSize);
|
|
let cursor = rCursor(doc, start, to), range = null;
|
|
while (!cursor.next().done)
|
|
range = cursor.value;
|
|
if (range && (start == from || range.from > start + 10))
|
|
return range;
|
|
if (start == from)
|
|
return null;
|
|
}
|
|
}
|
|
return {
|
|
findNext: function() {
|
|
return this.find(false);
|
|
},
|
|
findPrevious: function() {
|
|
return this.find(true);
|
|
},
|
|
find: function(back) {
|
|
var doc = cm.cm6.state.doc;
|
|
if (back) {
|
|
let endAt = last ? afterEmptyMatch ? last.to - 1 : last.from : firstOffset;
|
|
last = prevMatchInRange(0, endAt);
|
|
} else {
|
|
let startFrom = last ? afterEmptyMatch ? last.to + 1 : last.to : firstOffset;
|
|
last = nextMatch(startFrom);
|
|
}
|
|
lastCM5Result = last && {
|
|
from: posFromIndex(doc, last.from),
|
|
to: posFromIndex(doc, last.to),
|
|
match: last.match
|
|
};
|
|
afterEmptyMatch = last ? last.from == last.to : false;
|
|
return last && last.match;
|
|
},
|
|
from: function() {
|
|
return lastCM5Result === null || lastCM5Result === void 0 ? void 0 : lastCM5Result.from;
|
|
},
|
|
to: function() {
|
|
return lastCM5Result === null || lastCM5Result === void 0 ? void 0 : lastCM5Result.to;
|
|
},
|
|
replace: function(text) {
|
|
if (last) {
|
|
dispatchChange(cm, {
|
|
changes: { from: last.from, to: last.to, insert: text }
|
|
});
|
|
last.to = last.from + text.length;
|
|
if (lastCM5Result) {
|
|
lastCM5Result.to = posFromIndex(cm.cm6.state.doc, last.to);
|
|
}
|
|
}
|
|
},
|
|
get match() {
|
|
return lastCM5Result && lastCM5Result.match;
|
|
}
|
|
};
|
|
}
|
|
findPosV(start, amount, unit, goalColumn) {
|
|
let { cm6 } = this;
|
|
const doc = cm6.state.doc;
|
|
let pixels = unit == "page" ? cm6.dom.clientHeight : 0;
|
|
const startOffset = indexFromPos(doc, start);
|
|
let range = import_state.EditorSelection.cursor(startOffset, 1, void 0, goalColumn);
|
|
let count = Math.round(Math.abs(amount));
|
|
for (let i = 0; i < count; i++) {
|
|
if (unit == "page") {
|
|
range = cm6.moveVertically(range, amount > 0, pixels);
|
|
} else if (unit == "line") {
|
|
range = cm6.moveVertically(range, amount > 0);
|
|
}
|
|
}
|
|
let pos = posFromIndex(doc, range.head);
|
|
if (amount < 0 && range.head == 0 && goalColumn != 0 && start.line == 0 && start.ch != 0 || amount > 0 && range.head == doc.length && pos.ch != goalColumn && start.line == pos.line) {
|
|
pos.hitSide = true;
|
|
}
|
|
return pos;
|
|
}
|
|
charCoords(pos, mode) {
|
|
var rect = this.cm6.contentDOM.getBoundingClientRect();
|
|
var offset = indexFromPos(this.cm6.state.doc, pos);
|
|
var coords = this.cm6.coordsAtPos(offset);
|
|
var d = -rect.top;
|
|
return { left: ((coords === null || coords === void 0 ? void 0 : coords.left) || 0) - rect.left, top: ((coords === null || coords === void 0 ? void 0 : coords.top) || 0) + d, bottom: ((coords === null || coords === void 0 ? void 0 : coords.bottom) || 0) + d };
|
|
}
|
|
coordsChar(coords, mode) {
|
|
var rect = this.cm6.contentDOM.getBoundingClientRect();
|
|
var offset = this.cm6.posAtCoords({ x: coords.left + rect.left, y: coords.top + rect.top }) || 0;
|
|
return posFromIndex(this.cm6.state.doc, offset);
|
|
}
|
|
getScrollInfo() {
|
|
var scroller = this.cm6.scrollDOM;
|
|
return {
|
|
left: scroller.scrollLeft,
|
|
top: scroller.scrollTop,
|
|
height: scroller.scrollHeight,
|
|
width: scroller.scrollWidth,
|
|
clientHeight: scroller.clientHeight,
|
|
clientWidth: scroller.clientWidth
|
|
};
|
|
}
|
|
scrollTo(x, y) {
|
|
if (x != null)
|
|
this.cm6.scrollDOM.scrollLeft = x;
|
|
if (y != null)
|
|
this.cm6.scrollDOM.scrollTop = y;
|
|
}
|
|
scrollIntoView(pos, margin) {
|
|
if (pos) {
|
|
var offset = this.indexFromPos(pos);
|
|
this.cm6.dispatch({
|
|
effects: import_view.EditorView.scrollIntoView(offset)
|
|
});
|
|
} else {
|
|
this.cm6.dispatch({ scrollIntoView: true, userEvent: "scroll" });
|
|
}
|
|
}
|
|
getWrapperElement() {
|
|
return this.cm6.dom;
|
|
}
|
|
// for tests
|
|
getMode() {
|
|
return { name: this.getOption("mode") };
|
|
}
|
|
setSize(w, h) {
|
|
this.cm6.dom.style.width = w + 4 + "px";
|
|
this.cm6.dom.style.height = h + "px";
|
|
this.refresh();
|
|
}
|
|
refresh() {
|
|
this.cm6.measure();
|
|
}
|
|
// event listeners
|
|
destroy() {
|
|
this.removeOverlay();
|
|
}
|
|
getLastEditEnd() {
|
|
return this.posFromIndex(this.$lastChangeEndOffset);
|
|
}
|
|
onChange(update) {
|
|
if (this.$lineHandleChanges) {
|
|
this.$lineHandleChanges.push(update);
|
|
}
|
|
for (let i in this.marks) {
|
|
let m = this.marks[i];
|
|
m.update(update.changes);
|
|
}
|
|
if (this.virtualSelection) {
|
|
this.virtualSelection.ranges = this.virtualSelection.ranges.map((range) => range.map(update.changes));
|
|
}
|
|
var curOp = this.curOp = this.curOp || {};
|
|
update.changes.iterChanges((fromA, toA, fromB, toB, text) => {
|
|
if (curOp.$changeStart == null || curOp.$changeStart > fromB)
|
|
curOp.$changeStart = fromB;
|
|
this.$lastChangeEndOffset = toB;
|
|
var change = { text: text.toJSON() };
|
|
if (!curOp.lastChange) {
|
|
curOp.lastChange = curOp.change = change;
|
|
} else {
|
|
curOp.lastChange.next = curOp.lastChange = change;
|
|
}
|
|
}, true);
|
|
if (!curOp.changeHandlers)
|
|
curOp.changeHandlers = this._handlers["change"] && this._handlers["change"].slice();
|
|
}
|
|
onSelectionChange() {
|
|
var curOp = this.curOp = this.curOp || {};
|
|
if (!curOp.cursorActivityHandlers)
|
|
curOp.cursorActivityHandlers = this._handlers["cursorActivity"] && this._handlers["cursorActivity"].slice();
|
|
this.curOp.cursorActivity = true;
|
|
}
|
|
operation(fn, force) {
|
|
if (!this.curOp)
|
|
this.curOp = { $d: 0 };
|
|
this.curOp.$d++;
|
|
try {
|
|
var result = fn();
|
|
} finally {
|
|
if (this.curOp) {
|
|
this.curOp.$d--;
|
|
if (!this.curOp.$d)
|
|
this.onBeforeEndOperation();
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
onBeforeEndOperation() {
|
|
var op = this.curOp;
|
|
var scrollIntoView2 = false;
|
|
if (op) {
|
|
if (op.change) {
|
|
signalTo(op.changeHandlers, this, op.change);
|
|
}
|
|
if (op && op.cursorActivity) {
|
|
signalTo(op.cursorActivityHandlers, this, null);
|
|
if (op.isVimOp)
|
|
scrollIntoView2 = true;
|
|
}
|
|
this.curOp = null;
|
|
}
|
|
if (scrollIntoView2)
|
|
this.scrollIntoView();
|
|
}
|
|
moveH(increment, unit) {
|
|
if (unit == "char") {
|
|
var cur2 = this.getCursor();
|
|
this.setCursor(cur2.line, cur2.ch + increment);
|
|
}
|
|
}
|
|
setOption(name, val) {
|
|
switch (name) {
|
|
case "keyMap":
|
|
this.state.keyMap = val;
|
|
break;
|
|
case "textwidth":
|
|
this.state.textwidth = val;
|
|
break;
|
|
}
|
|
}
|
|
getOption(name) {
|
|
switch (name) {
|
|
case "firstLineNumber":
|
|
return 1;
|
|
case "tabSize":
|
|
return this.cm6.state.tabSize || 4;
|
|
case "readOnly":
|
|
return this.cm6.state.readOnly;
|
|
case "indentWithTabs":
|
|
return this.cm6.state.facet(import_language.indentUnit) == " ";
|
|
// TODO
|
|
case "indentUnit":
|
|
return this.cm6.state.facet(import_language.indentUnit).length || 2;
|
|
case "textwidth":
|
|
return this.state.textwidth;
|
|
// for tests
|
|
case "keyMap":
|
|
return this.state.keyMap || "vim";
|
|
}
|
|
}
|
|
toggleOverwrite(on2) {
|
|
this.state.overwrite = on2;
|
|
}
|
|
getTokenTypeAt(pos) {
|
|
var _a;
|
|
var offset = this.indexFromPos(pos);
|
|
var tree = (0, import_language.ensureSyntaxTree)(this.cm6.state, offset);
|
|
var node = tree === null || tree === void 0 ? void 0 : tree.resolve(offset);
|
|
var type = ((_a = node === null || node === void 0 ? void 0 : node.type) === null || _a === void 0 ? void 0 : _a.name) || "";
|
|
if (/comment/i.test(type))
|
|
return "comment";
|
|
if (/string/i.test(type))
|
|
return "string";
|
|
return "";
|
|
}
|
|
overWriteSelection(text) {
|
|
var doc = this.cm6.state.doc;
|
|
var sel = this.cm6.state.selection;
|
|
var ranges = sel.ranges.map((x) => {
|
|
if (x.empty) {
|
|
var ch = x.to < doc.length ? doc.sliceString(x.from, x.to + 1) : "";
|
|
if (ch && !/\n/.test(ch))
|
|
return import_state.EditorSelection.range(x.from, x.to + 1);
|
|
}
|
|
return x;
|
|
});
|
|
this.cm6.dispatch({
|
|
selection: import_state.EditorSelection.create(ranges, sel.mainIndex)
|
|
});
|
|
this.replaceSelection(text);
|
|
}
|
|
/*** multiselect ****/
|
|
isInMultiSelectMode() {
|
|
return this.cm6.state.selection.ranges.length > 1;
|
|
}
|
|
virtualSelectionMode() {
|
|
return !!this.virtualSelection;
|
|
}
|
|
forEachSelection(command) {
|
|
var selection = this.cm6.state.selection;
|
|
this.virtualSelection = import_state.EditorSelection.create(selection.ranges, selection.mainIndex);
|
|
for (var i = 0; i < this.virtualSelection.ranges.length; i++) {
|
|
var range = this.virtualSelection.ranges[i];
|
|
if (!range)
|
|
continue;
|
|
this.cm6.dispatch({ selection: import_state.EditorSelection.create([range]) });
|
|
command();
|
|
this.virtualSelection.ranges[i] = this.cm6.state.selection.ranges[0];
|
|
}
|
|
this.cm6.dispatch({ selection: this.virtualSelection });
|
|
this.virtualSelection = null;
|
|
}
|
|
hardWrap(options) {
|
|
return hardWrap(this, options);
|
|
}
|
|
};
|
|
CodeMirror.isMac = typeof navigator != "undefined" && /* @__PURE__ */ /Mac/.test(navigator.platform);
|
|
CodeMirror.Pos = Pos;
|
|
CodeMirror.StringStream = import_language.StringStream;
|
|
CodeMirror.commands = {
|
|
cursorCharLeft: function(cm) {
|
|
(0, import_commands.cursorCharLeft)(cm.cm6);
|
|
},
|
|
redo: function(cm) {
|
|
runHistoryCommand(cm, false);
|
|
},
|
|
undo: function(cm) {
|
|
runHistoryCommand(cm, true);
|
|
},
|
|
newlineAndIndent: function(cm) {
|
|
(0, import_commands.insertNewlineAndIndent)({
|
|
state: cm.cm6.state,
|
|
dispatch: (tr) => {
|
|
return dispatchChange(cm, tr);
|
|
}
|
|
});
|
|
},
|
|
indentAuto: function(cm) {
|
|
(0, import_commands.indentSelection)(cm.cm6);
|
|
},
|
|
newlineAndIndentContinueComment: void 0,
|
|
save: void 0
|
|
};
|
|
CodeMirror.isWordChar = function(ch) {
|
|
return wordChar.test(ch);
|
|
};
|
|
CodeMirror.keys = keys;
|
|
CodeMirror.addClass = function(el, str) {
|
|
};
|
|
CodeMirror.rmClass = function(el, str) {
|
|
};
|
|
CodeMirror.e_preventDefault = function(e) {
|
|
e.preventDefault();
|
|
};
|
|
CodeMirror.e_stop = function(e) {
|
|
var _a, _b;
|
|
(_a = e === null || e === void 0 ? void 0 : e.stopPropagation) === null || _a === void 0 ? void 0 : _a.call(e);
|
|
(_b = e === null || e === void 0 ? void 0 : e.preventDefault) === null || _b === void 0 ? void 0 : _b.call(e);
|
|
};
|
|
CodeMirror.lookupKey = function lookupKey(key, map, handle) {
|
|
var result = CodeMirror.keys[key];
|
|
if (!result && /^Arrow/.test(key))
|
|
result = CodeMirror.keys[key.slice(5)];
|
|
if (result)
|
|
handle(result);
|
|
};
|
|
CodeMirror.on = on;
|
|
CodeMirror.off = off;
|
|
CodeMirror.signal = signal;
|
|
CodeMirror.findMatchingTag = findMatchingTag;
|
|
CodeMirror.findEnclosingTag = findEnclosingTag;
|
|
CodeMirror.keyName = void 0;
|
|
function dialogDiv(cm, template, bottom) {
|
|
var dialog = document.createElement("div");
|
|
dialog.appendChild(template);
|
|
return dialog;
|
|
}
|
|
function closeNotification(cm, newVal) {
|
|
if (cm.state.currentNotificationClose)
|
|
cm.state.currentNotificationClose();
|
|
cm.state.currentNotificationClose = newVal;
|
|
}
|
|
function openNotification(cm, template, options) {
|
|
closeNotification(cm, close);
|
|
var dialog = dialogDiv(cm, template, options && options.bottom);
|
|
var closed = false;
|
|
var doneTimer;
|
|
var duration = options && typeof options.duration !== "undefined" ? options.duration : 5e3;
|
|
function close() {
|
|
if (closed)
|
|
return;
|
|
closed = true;
|
|
clearTimeout(doneTimer);
|
|
dialog.remove();
|
|
hideDialog(cm, dialog);
|
|
}
|
|
dialog.onclick = function(e) {
|
|
e.preventDefault();
|
|
close();
|
|
};
|
|
showDialog(cm, dialog);
|
|
if (duration)
|
|
doneTimer = setTimeout(close, duration);
|
|
return close;
|
|
}
|
|
function showDialog(cm, dialog) {
|
|
var oldDialog = cm.state.dialog;
|
|
cm.state.dialog = dialog;
|
|
dialog.style.flex = "1";
|
|
if (dialog && oldDialog !== dialog) {
|
|
if (oldDialog && oldDialog.contains(document.activeElement))
|
|
cm.focus();
|
|
if (oldDialog && oldDialog.parentElement) {
|
|
oldDialog.parentElement.replaceChild(dialog, oldDialog);
|
|
} else if (oldDialog) {
|
|
oldDialog.remove();
|
|
}
|
|
CodeMirror.signal(cm, "dialog");
|
|
}
|
|
}
|
|
function hideDialog(cm, dialog) {
|
|
if (cm.state.dialog == dialog) {
|
|
cm.state.dialog = null;
|
|
CodeMirror.signal(cm, "dialog");
|
|
}
|
|
}
|
|
function openDialog(me, template, callback, options) {
|
|
if (!options)
|
|
options = {};
|
|
closeNotification(me, void 0);
|
|
var dialog = dialogDiv(me, template, options.bottom);
|
|
var closed = false;
|
|
showDialog(me, dialog);
|
|
function close(newVal) {
|
|
if (typeof newVal == "string") {
|
|
inp.value = newVal;
|
|
} else {
|
|
if (closed)
|
|
return;
|
|
closed = true;
|
|
hideDialog(me, dialog);
|
|
if (!me.state.dialog)
|
|
me.focus();
|
|
if (options.onClose)
|
|
options.onClose(dialog);
|
|
}
|
|
}
|
|
var inp = dialog.getElementsByTagName("input")[0];
|
|
if (inp) {
|
|
if (options.value) {
|
|
inp.value = options.value;
|
|
if (options.selectValueOnOpen !== false)
|
|
inp.select();
|
|
}
|
|
if (options.onInput)
|
|
CodeMirror.on(inp, "input", function(e) {
|
|
options.onInput(e, inp.value, close);
|
|
});
|
|
if (options.onKeyUp)
|
|
CodeMirror.on(inp, "keyup", function(e) {
|
|
options.onKeyUp(e, inp.value, close);
|
|
});
|
|
CodeMirror.on(inp, "keydown", function(e) {
|
|
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) {
|
|
return;
|
|
}
|
|
if (e.keyCode == 13)
|
|
callback && callback(inp.value);
|
|
if (e.keyCode == 27 || options.closeOnEnter !== false && e.keyCode == 13) {
|
|
inp.blur();
|
|
CodeMirror.e_stop(e);
|
|
close();
|
|
}
|
|
});
|
|
if (options.closeOnBlur !== false)
|
|
CodeMirror.on(inp, "blur", function() {
|
|
setTimeout(function() {
|
|
if (document.activeElement === inp)
|
|
return;
|
|
close();
|
|
});
|
|
});
|
|
inp.focus();
|
|
}
|
|
return close;
|
|
}
|
|
var matching = { "(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<" };
|
|
function bracketRegex(config3) {
|
|
return config3 && config3.bracketRegex || /[(){}[\]]/;
|
|
}
|
|
function scanForBracket(cm, where, dir, style, config3) {
|
|
var maxScanLen = config3 && config3.maxScanLineLength || 1e4;
|
|
var maxScanLines = config3 && config3.maxScanLines || 1e3;
|
|
var stack = [];
|
|
var re = bracketRegex(config3);
|
|
var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) : Math.max(cm.firstLine() - 1, where.line - maxScanLines);
|
|
for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
|
|
var line = cm.getLine(lineNo);
|
|
if (!line)
|
|
continue;
|
|
var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
|
|
if (line.length > maxScanLen)
|
|
continue;
|
|
if (lineNo == where.line)
|
|
pos = where.ch - (dir < 0 ? 1 : 0);
|
|
for (; pos != end; pos += dir) {
|
|
var ch = line.charAt(pos);
|
|
if (re.test(ch)) {
|
|
var match = matching[ch];
|
|
if (match && match.charAt(1) == ">" == dir > 0)
|
|
stack.push(ch);
|
|
else if (!stack.length)
|
|
return { pos: new Pos(lineNo, pos), ch };
|
|
else
|
|
stack.pop();
|
|
}
|
|
}
|
|
}
|
|
return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
|
|
}
|
|
function findMatchingTag(cm, pos) {
|
|
return null;
|
|
}
|
|
function findEnclosingTag(cm, pos) {
|
|
var _a, _b;
|
|
var state = cm.cm6.state;
|
|
var offset = cm.indexFromPos(pos);
|
|
if (offset < state.doc.length) {
|
|
var text = state.sliceDoc(offset, offset + 1);
|
|
if (text == "<")
|
|
offset++;
|
|
}
|
|
var tree = (0, import_language.ensureSyntaxTree)(state, offset);
|
|
var node = (tree === null || tree === void 0 ? void 0 : tree.resolve(offset)) || null;
|
|
while (node) {
|
|
if (((_a = node.firstChild) === null || _a === void 0 ? void 0 : _a.type.name) == "OpenTag" && ((_b = node.lastChild) === null || _b === void 0 ? void 0 : _b.type.name) == "CloseTag") {
|
|
return {
|
|
open: convertRange(state.doc, node.firstChild),
|
|
close: convertRange(state.doc, node.lastChild)
|
|
};
|
|
}
|
|
node = node.parent;
|
|
}
|
|
}
|
|
function convertRange(doc, cm6Range) {
|
|
return {
|
|
from: posFromIndex(doc, cm6Range.from),
|
|
to: posFromIndex(doc, cm6Range.to)
|
|
};
|
|
}
|
|
var Marker = class {
|
|
constructor(cm, offset, assoc) {
|
|
this.cm = cm;
|
|
this.id = cm.$mid++;
|
|
this.offset = offset;
|
|
this.assoc = assoc;
|
|
cm.marks[this.id] = this;
|
|
}
|
|
clear() {
|
|
delete this.cm.marks[this.id];
|
|
}
|
|
find() {
|
|
if (this.offset == null)
|
|
return null;
|
|
return this.cm.posFromIndex(this.offset);
|
|
}
|
|
update(change) {
|
|
if (this.offset != null)
|
|
this.offset = change.mapPos(this.offset, this.assoc, import_state.MapMode.TrackDel);
|
|
}
|
|
};
|
|
function hardWrap(cm, options) {
|
|
var _a;
|
|
var max = options.column || cm.getOption("textwidth") || 80;
|
|
var allowMerge = options.allowMerge != false;
|
|
var row = Math.min(options.from, options.to);
|
|
var endRow = Math.max(options.from, options.to);
|
|
while (row <= endRow) {
|
|
var line = cm.getLine(row);
|
|
if (line.length > max) {
|
|
var space2 = findSpace(line, max, 5);
|
|
if (space2) {
|
|
var indentation = (_a = /^\s*/.exec(line)) === null || _a === void 0 ? void 0 : _a[0];
|
|
cm.replaceRange("\n" + indentation, new Pos(row, space2.start), new Pos(row, space2.end));
|
|
}
|
|
endRow++;
|
|
} else if (allowMerge && /\S/.test(line) && row != endRow) {
|
|
var nextLine = cm.getLine(row + 1);
|
|
if (nextLine && /\S/.test(nextLine)) {
|
|
var trimmedLine = line.replace(/\s+$/, "");
|
|
var trimmedNextLine = nextLine.replace(/^\s+/, "");
|
|
var mergedLine = trimmedLine + " " + trimmedNextLine;
|
|
var space2 = findSpace(mergedLine, max, 5);
|
|
if (space2 && space2.start > trimmedLine.length || mergedLine.length < max) {
|
|
cm.replaceRange(" ", new Pos(row, trimmedLine.length), new Pos(row + 1, nextLine.length - trimmedNextLine.length));
|
|
row--;
|
|
endRow--;
|
|
} else if (trimmedLine.length < line.length) {
|
|
cm.replaceRange("", new Pos(row, trimmedLine.length), new Pos(row, line.length));
|
|
}
|
|
}
|
|
}
|
|
row++;
|
|
}
|
|
return row;
|
|
function findSpace(line2, max2, min) {
|
|
if (line2.length < max2)
|
|
return;
|
|
var before = line2.slice(0, max2);
|
|
var after = line2.slice(max2);
|
|
var spaceAfter = /^(?:(\s+)|(\S+)(\s+))/.exec(after);
|
|
var spaceBefore = /(?:(\s+)|(\s+)(\S+))$/.exec(before);
|
|
var start = 0;
|
|
var end = 0;
|
|
if (spaceBefore && !spaceBefore[2]) {
|
|
start = max2 - spaceBefore[1].length;
|
|
end = max2;
|
|
}
|
|
if (spaceAfter && !spaceAfter[2]) {
|
|
if (!start)
|
|
start = max2;
|
|
end = max2 + spaceAfter[1].length;
|
|
}
|
|
if (start) {
|
|
return {
|
|
start,
|
|
end
|
|
};
|
|
}
|
|
if (spaceBefore && spaceBefore[2] && spaceBefore.index > min) {
|
|
return {
|
|
start: spaceBefore.index,
|
|
end: spaceBefore.index + spaceBefore[2].length
|
|
};
|
|
}
|
|
if (spaceAfter && spaceAfter[2]) {
|
|
start = max2 + spaceAfter[2].length;
|
|
return {
|
|
start,
|
|
end: start + spaceAfter[3].length
|
|
};
|
|
}
|
|
}
|
|
}
|
|
var getDrawSelectionConfig2 = View.getDrawSelectionConfig || /* @__PURE__ */ function() {
|
|
let defaultConfig = { cursorBlinkRate: 1200 };
|
|
return function() {
|
|
return defaultConfig;
|
|
};
|
|
}();
|
|
var Piece = class {
|
|
constructor(left, top, height, fontFamily, fontSize, fontWeight, color, className, letter, partial) {
|
|
this.left = left;
|
|
this.top = top;
|
|
this.height = height;
|
|
this.fontFamily = fontFamily;
|
|
this.fontSize = fontSize;
|
|
this.fontWeight = fontWeight;
|
|
this.color = color;
|
|
this.className = className;
|
|
this.letter = letter;
|
|
this.partial = partial;
|
|
}
|
|
draw() {
|
|
let elt = document.createElement("div");
|
|
elt.className = this.className;
|
|
this.adjust(elt);
|
|
return elt;
|
|
}
|
|
adjust(elt) {
|
|
elt.style.left = this.left + "px";
|
|
elt.style.top = this.top + "px";
|
|
elt.style.height = this.height + "px";
|
|
elt.style.lineHeight = this.height + "px";
|
|
elt.style.fontFamily = this.fontFamily;
|
|
elt.style.fontSize = this.fontSize;
|
|
elt.style.fontWeight = this.fontWeight;
|
|
elt.style.color = this.partial ? "transparent" : this.color;
|
|
elt.className = this.className;
|
|
elt.textContent = this.letter;
|
|
}
|
|
eq(p) {
|
|
return this.left == p.left && this.top == p.top && this.height == p.height && this.fontFamily == p.fontFamily && this.fontSize == p.fontSize && this.fontWeight == p.fontWeight && this.color == p.color && this.className == p.className && this.letter == p.letter;
|
|
}
|
|
};
|
|
var BlockCursorPlugin = class {
|
|
constructor(view, cm) {
|
|
this.view = view;
|
|
this.rangePieces = [];
|
|
this.cursors = [];
|
|
this.cm = cm;
|
|
this.measureReq = { read: this.readPos.bind(this), write: this.drawSel.bind(this) };
|
|
this.cursorLayer = view.scrollDOM.appendChild(document.createElement("div"));
|
|
this.cursorLayer.className = "cm-cursorLayer cm-vimCursorLayer";
|
|
this.cursorLayer.setAttribute("aria-hidden", "true");
|
|
view.requestMeasure(this.measureReq);
|
|
this.setBlinkRate();
|
|
}
|
|
setBlinkRate() {
|
|
let config3 = getDrawSelectionConfig2(this.cm.cm6.state);
|
|
let blinkRate = config3.cursorBlinkRate;
|
|
this.cursorLayer.style.animationDuration = blinkRate + "ms";
|
|
}
|
|
update(update) {
|
|
if (update.selectionSet || update.geometryChanged || update.viewportChanged) {
|
|
this.view.requestMeasure(this.measureReq);
|
|
this.cursorLayer.style.animationName = this.cursorLayer.style.animationName == "cm-blink" ? "cm-blink2" : "cm-blink";
|
|
}
|
|
if (configChanged(update))
|
|
this.setBlinkRate();
|
|
}
|
|
scheduleRedraw() {
|
|
this.view.requestMeasure(this.measureReq);
|
|
}
|
|
readPos() {
|
|
let { state } = this.view;
|
|
let cursors = [];
|
|
for (let r of state.selection.ranges) {
|
|
let prim = r == state.selection.main;
|
|
let piece = measureCursor(this.cm, this.view, r, prim);
|
|
if (piece)
|
|
cursors.push(piece);
|
|
}
|
|
return { cursors };
|
|
}
|
|
drawSel({ cursors }) {
|
|
if (cursors.length != this.cursors.length || cursors.some((c, i) => !c.eq(this.cursors[i]))) {
|
|
let oldCursors = this.cursorLayer.children;
|
|
if (oldCursors.length !== cursors.length) {
|
|
this.cursorLayer.textContent = "";
|
|
for (const c of cursors)
|
|
this.cursorLayer.appendChild(c.draw());
|
|
} else {
|
|
cursors.forEach((c, idx) => c.adjust(oldCursors[idx]));
|
|
}
|
|
this.cursors = cursors;
|
|
}
|
|
}
|
|
destroy() {
|
|
this.cursorLayer.remove();
|
|
}
|
|
};
|
|
function configChanged(update) {
|
|
return getDrawSelectionConfig2(update.startState) != getDrawSelectionConfig2(update.state);
|
|
}
|
|
var themeSpec = {
|
|
".cm-vimMode .cm-line": {
|
|
"& ::selection": { backgroundColor: "transparent !important" },
|
|
"&::selection": { backgroundColor: "transparent !important" },
|
|
caretColor: "transparent !important"
|
|
},
|
|
".cm-fat-cursor": {
|
|
position: "absolute",
|
|
background: "#ff9696",
|
|
border: "none",
|
|
whiteSpace: "pre"
|
|
},
|
|
"&:not(.cm-focused) .cm-fat-cursor": {
|
|
background: "none",
|
|
outline: "solid 1px #ff9696",
|
|
color: "transparent !important"
|
|
}
|
|
};
|
|
var hideNativeSelection = /* @__PURE__ */ import_state.Prec.highest(/* @__PURE__ */ import_view.EditorView.theme(themeSpec));
|
|
function getBase(view) {
|
|
let rect = view.scrollDOM.getBoundingClientRect();
|
|
let left = view.textDirection == import_view.Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth;
|
|
return { left: left - view.scrollDOM.scrollLeft * view.scaleX, top: rect.top - view.scrollDOM.scrollTop * view.scaleY };
|
|
}
|
|
function measureCursor(cm, view, cursor, primary) {
|
|
var _a, _b, _c, _d;
|
|
let head = cursor.head;
|
|
let fatCursor = false;
|
|
let hCoeff = 1;
|
|
let vim2 = cm.state.vim;
|
|
if (vim2 && (!vim2.insertMode || cm.state.overwrite)) {
|
|
fatCursor = true;
|
|
if (vim2.visualBlock && !primary)
|
|
return null;
|
|
if (cursor.anchor < cursor.head) {
|
|
let letter = head < view.state.doc.length && view.state.sliceDoc(head, head + 1);
|
|
if (letter != "\n")
|
|
head--;
|
|
}
|
|
if (cm.state.overwrite)
|
|
hCoeff = 0.2;
|
|
else if (vim2.status)
|
|
hCoeff = 0.5;
|
|
}
|
|
if (fatCursor) {
|
|
let letter = head < view.state.doc.length && view.state.sliceDoc(head, head + 1);
|
|
if (letter && (/[\uDC00-\uDFFF]/.test(letter) && head > 1)) {
|
|
head--;
|
|
letter = view.state.sliceDoc(head, head + 1);
|
|
}
|
|
let pos = view.coordsAtPos(head, 1);
|
|
if (!pos)
|
|
return null;
|
|
let base = getBase(view);
|
|
let domAtPos = view.domAtPos(head);
|
|
let node = domAtPos ? domAtPos.node : view.contentDOM;
|
|
if (node instanceof Text && domAtPos.offset >= node.data.length) {
|
|
if ((_a = node.parentElement) === null || _a === void 0 ? void 0 : _a.nextSibling) {
|
|
node = (_b = node.parentElement) === null || _b === void 0 ? void 0 : _b.nextSibling;
|
|
domAtPos = { node, offset: 0 };
|
|
}
|
|
}
|
|
while (domAtPos && domAtPos.node instanceof HTMLElement) {
|
|
node = domAtPos.node;
|
|
domAtPos = { node: domAtPos.node.childNodes[domAtPos.offset], offset: 0 };
|
|
}
|
|
if (!(node instanceof HTMLElement)) {
|
|
if (!node.parentNode)
|
|
return null;
|
|
node = node.parentNode;
|
|
}
|
|
let style = getComputedStyle(node);
|
|
let left = pos.left;
|
|
let charCoords = (_d = (_c = view).coordsForChar) === null || _d === void 0 ? void 0 : _d.call(_c, head);
|
|
if (charCoords) {
|
|
left = charCoords.left;
|
|
}
|
|
if (!letter || letter == "\n" || letter == "\r") {
|
|
letter = "\xA0";
|
|
} else if (letter == " ") {
|
|
letter = "\xA0";
|
|
var nextPos = view.coordsAtPos(head + 1, -1);
|
|
if (nextPos) {
|
|
left = nextPos.left - (nextPos.left - pos.left) / parseInt(style.tabSize);
|
|
}
|
|
} else if (/[\uD800-\uDBFF]/.test(letter) && head < view.state.doc.length - 1) {
|
|
letter += view.state.sliceDoc(head + 1, head + 2);
|
|
}
|
|
let h = pos.bottom - pos.top;
|
|
return new Piece((left - base.left) / view.scaleX, (pos.top - base.top + h * (1 - hCoeff)) / view.scaleY, h * hCoeff / view.scaleY, style.fontFamily, style.fontSize, style.fontWeight, style.color, primary ? "cm-fat-cursor cm-cursor-primary" : "cm-fat-cursor cm-cursor-secondary", letter, hCoeff != 1);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
var FIREFOX_LINUX = typeof navigator != "undefined" && /* @__PURE__ */ /linux/i.test(navigator.platform) && /* @__PURE__ */ / Gecko\/\d+/.exec(navigator.userAgent);
|
|
var Vim = /* @__PURE__ */ initVim(CodeMirror);
|
|
var HighlightMargin = 250;
|
|
var vimStyle = /* @__PURE__ */ import_view.EditorView.baseTheme({
|
|
".cm-vimMode .cm-cursorLayer:not(.cm-vimCursorLayer)": {
|
|
display: "none"
|
|
},
|
|
".cm-vim-panel": {
|
|
padding: "0px 10px",
|
|
fontFamily: "monospace",
|
|
minHeight: "1.3em",
|
|
display: "flex"
|
|
},
|
|
".cm-vim-panel input": {
|
|
border: "none",
|
|
outline: "none",
|
|
backgroundColor: "inherit"
|
|
},
|
|
"&light .cm-searchMatch": { backgroundColor: "#ffff0054" },
|
|
"&dark .cm-searchMatch": { backgroundColor: "#00ffff8a" }
|
|
});
|
|
var vimPlugin = /* @__PURE__ */ import_view.ViewPlugin.fromClass(class {
|
|
constructor(view) {
|
|
this.status = "";
|
|
this.query = null;
|
|
this.decorations = import_view.Decoration.none;
|
|
this.waitForCopy = false;
|
|
this.lastKeydown = "";
|
|
this.useNextTextInput = false;
|
|
this.compositionText = "";
|
|
this.view = view;
|
|
const cm = this.cm = new CodeMirror(view);
|
|
Vim.enterVimMode(this.cm);
|
|
this.view.cm = this.cm;
|
|
this.cm.state.vimPlugin = this;
|
|
this.blockCursor = new BlockCursorPlugin(view, cm);
|
|
this.updateClass();
|
|
this.cm.on("vim-command-done", () => {
|
|
if (cm.state.vim)
|
|
cm.state.vim.status = "";
|
|
this.blockCursor.scheduleRedraw();
|
|
this.updateStatus();
|
|
});
|
|
this.cm.on("vim-mode-change", (e) => {
|
|
if (!cm.state.vim)
|
|
return;
|
|
cm.state.vim.mode = e.mode;
|
|
if (e.subMode) {
|
|
cm.state.vim.mode += " block";
|
|
}
|
|
cm.state.vim.status = "";
|
|
this.blockCursor.scheduleRedraw();
|
|
this.updateClass();
|
|
this.updateStatus();
|
|
});
|
|
this.cm.on("dialog", () => {
|
|
if (this.cm.state.statusbar) {
|
|
this.updateStatus();
|
|
} else {
|
|
view.dispatch({
|
|
effects: showVimPanel.of(!!this.cm.state.dialog)
|
|
});
|
|
}
|
|
});
|
|
this.dom = document.createElement("span");
|
|
this.spacer = document.createElement("span");
|
|
this.spacer.style.flex = "1";
|
|
this.statusButton = document.createElement("span");
|
|
this.statusButton.onclick = (e) => {
|
|
Vim.handleKey(this.cm, "<Esc>", "user");
|
|
this.cm.focus();
|
|
};
|
|
this.statusButton.style.cssText = "cursor: pointer";
|
|
}
|
|
update(update) {
|
|
var _a;
|
|
if ((update.viewportChanged || update.docChanged) && this.query) {
|
|
this.highlight(this.query);
|
|
}
|
|
if (update.docChanged) {
|
|
this.cm.onChange(update);
|
|
}
|
|
if (update.selectionSet) {
|
|
this.cm.onSelectionChange();
|
|
}
|
|
if (update.viewportChanged) ;
|
|
if (this.cm.curOp && !this.cm.curOp.isVimOp) {
|
|
this.cm.onBeforeEndOperation();
|
|
}
|
|
if (update.transactions) {
|
|
for (let tr of update.transactions)
|
|
for (let effect of tr.effects) {
|
|
if (effect.is(import_search.setSearchQuery)) {
|
|
let forVim = (_a = effect.value) === null || _a === void 0 ? void 0 : _a.forVim;
|
|
if (!forVim) {
|
|
this.highlight(null);
|
|
} else {
|
|
let query = effect.value.create();
|
|
this.highlight(query);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this.blockCursor.update(update);
|
|
}
|
|
updateClass() {
|
|
const state = this.cm.state;
|
|
if (!state.vim || state.vim.insertMode && !state.overwrite)
|
|
this.view.scrollDOM.classList.remove("cm-vimMode");
|
|
else
|
|
this.view.scrollDOM.classList.add("cm-vimMode");
|
|
}
|
|
updateStatus() {
|
|
let dom = this.cm.state.statusbar;
|
|
let vim2 = this.cm.state.vim;
|
|
if (!dom || !vim2)
|
|
return;
|
|
let dialog = this.cm.state.dialog;
|
|
if (dialog) {
|
|
if (dialog.parentElement != dom) {
|
|
dom.textContent = "";
|
|
dom.appendChild(dialog);
|
|
}
|
|
} else {
|
|
dom.textContent = "";
|
|
var status = (vim2.mode || "normal").toUpperCase();
|
|
if (vim2.insertModeReturn)
|
|
status += "(C-O)";
|
|
this.statusButton.textContent = `--${status}--`;
|
|
dom.appendChild(this.statusButton);
|
|
dom.appendChild(this.spacer);
|
|
}
|
|
this.dom.textContent = vim2.status;
|
|
dom.appendChild(this.dom);
|
|
}
|
|
destroy() {
|
|
Vim.leaveVimMode(this.cm);
|
|
this.updateClass();
|
|
this.blockCursor.destroy();
|
|
delete this.view.cm;
|
|
}
|
|
highlight(query) {
|
|
this.query = query;
|
|
if (!query)
|
|
return this.decorations = import_view.Decoration.none;
|
|
let { view } = this;
|
|
let builder = new import_state.RangeSetBuilder();
|
|
for (let i = 0, ranges = view.visibleRanges, l = ranges.length; i < l; i++) {
|
|
let { from, to } = ranges[i];
|
|
while (i < l - 1 && to > ranges[i + 1].from - 2 * HighlightMargin)
|
|
to = ranges[++i].to;
|
|
query.highlight(view.state, from, to, (from2, to2) => {
|
|
builder.add(from2, to2, matchMark);
|
|
});
|
|
}
|
|
return this.decorations = builder.finish();
|
|
}
|
|
handleKey(e, view) {
|
|
const cm = this.cm;
|
|
let vim2 = cm.state.vim;
|
|
if (!vim2)
|
|
return;
|
|
const key = Vim.vimKeyFromEvent(e, vim2);
|
|
CodeMirror.signal(this.cm, "inputEvent", { type: "handleKey", key });
|
|
if (!key)
|
|
return;
|
|
if (key == "<Esc>" && !vim2.insertMode && !vim2.visualMode && this.query) {
|
|
const searchState = vim2.searchState_;
|
|
if (searchState) {
|
|
cm.removeOverlay(searchState.getOverlay());
|
|
searchState.setOverlay(null);
|
|
}
|
|
}
|
|
let isCopy = key === "<C-c>" && !CodeMirror.isMac;
|
|
if (isCopy && cm.somethingSelected()) {
|
|
this.waitForCopy = true;
|
|
return true;
|
|
}
|
|
vim2.status = (vim2.status || "") + key;
|
|
let result = Vim.multiSelectHandleKey(cm, key, "user");
|
|
vim2 = Vim.maybeInitVimState_(cm);
|
|
if (!result && vim2.insertMode && cm.state.overwrite) {
|
|
if (e.key && e.key.length == 1 && !/\n/.test(e.key)) {
|
|
result = true;
|
|
cm.overWriteSelection(e.key);
|
|
} else if (e.key == "Backspace") {
|
|
result = true;
|
|
CodeMirror.commands.cursorCharLeft(cm);
|
|
}
|
|
}
|
|
if (result) {
|
|
CodeMirror.signal(this.cm, "vim-keypress", key);
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.blockCursor.scheduleRedraw();
|
|
}
|
|
this.updateStatus();
|
|
return !!result;
|
|
}
|
|
}, {
|
|
eventHandlers: {
|
|
copy: function(e, view) {
|
|
if (!this.waitForCopy)
|
|
return;
|
|
this.waitForCopy = false;
|
|
Promise.resolve().then(() => {
|
|
var cm = this.cm;
|
|
var vim2 = cm.state.vim;
|
|
if (!vim2)
|
|
return;
|
|
if (vim2.insertMode) {
|
|
cm.setSelection(cm.getCursor(), cm.getCursor());
|
|
} else {
|
|
cm.operation(() => {
|
|
if (cm.curOp)
|
|
cm.curOp.isVimOp = true;
|
|
Vim.handleKey(cm, "<Esc>", "user");
|
|
});
|
|
}
|
|
});
|
|
},
|
|
compositionstart: function(e, view) {
|
|
this.useNextTextInput = true;
|
|
CodeMirror.signal(this.cm, "inputEvent", e);
|
|
},
|
|
compositionupdate: function(e, view) {
|
|
CodeMirror.signal(this.cm, "inputEvent", e);
|
|
},
|
|
compositionend: function(e, view) {
|
|
CodeMirror.signal(this.cm, "inputEvent", e);
|
|
},
|
|
keypress: function(e, view) {
|
|
CodeMirror.signal(this.cm, "inputEvent", e);
|
|
if (this.lastKeydown == "Dead")
|
|
this.handleKey(e, view);
|
|
},
|
|
keydown: function(e, view) {
|
|
CodeMirror.signal(this.cm, "inputEvent", e);
|
|
this.lastKeydown = e.key;
|
|
if (this.lastKeydown == "Unidentified" || this.lastKeydown == "Process" || this.lastKeydown == "Dead") {
|
|
this.useNextTextInput = true;
|
|
} else {
|
|
this.useNextTextInput = false;
|
|
this.handleKey(e, view);
|
|
}
|
|
}
|
|
},
|
|
provide: () => {
|
|
return [
|
|
import_view.EditorView.inputHandler.of((view, from, to, text) => {
|
|
var _a, _b;
|
|
var cm = getCM(view);
|
|
if (!cm)
|
|
return false;
|
|
var vim2 = (_a = cm.state) === null || _a === void 0 ? void 0 : _a.vim;
|
|
var vimPlugin2 = cm.state.vimPlugin;
|
|
if (vim2 && !vim2.insertMode && !((_b = cm.curOp) === null || _b === void 0 ? void 0 : _b.isVimOp)) {
|
|
if (text === "\0\0") {
|
|
return true;
|
|
}
|
|
CodeMirror.signal(cm, "inputEvent", {
|
|
type: "text",
|
|
text,
|
|
from,
|
|
to
|
|
});
|
|
if (text.length == 1 && vimPlugin2.useNextTextInput) {
|
|
if (vim2.expectLiteralNext && view.composing) {
|
|
vimPlugin2.compositionText = text;
|
|
return false;
|
|
}
|
|
if (vimPlugin2.compositionText) {
|
|
var toRemove = vimPlugin2.compositionText;
|
|
vimPlugin2.compositionText = "";
|
|
var head = view.state.selection.main.head;
|
|
var textInDoc = view.state.sliceDoc(head - toRemove.length, head);
|
|
if (toRemove === textInDoc) {
|
|
var pos = cm.getCursor();
|
|
cm.replaceRange("", cm.posFromIndex(head - toRemove.length), pos);
|
|
}
|
|
}
|
|
vimPlugin2.handleKey({
|
|
key: text,
|
|
preventDefault: () => {
|
|
},
|
|
stopPropagation: () => {
|
|
}
|
|
});
|
|
forceEndComposition(view);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
})
|
|
];
|
|
},
|
|
decorations: (v) => v.decorations
|
|
});
|
|
function forceEndComposition(view) {
|
|
var parent = view.scrollDOM.parentElement;
|
|
if (!parent)
|
|
return;
|
|
if (FIREFOX_LINUX) {
|
|
view.contentDOM.textContent = "\0\0";
|
|
view.contentDOM.dispatchEvent(new CustomEvent("compositionend"));
|
|
return;
|
|
}
|
|
var sibling = view.scrollDOM.nextSibling;
|
|
var selection = window.getSelection();
|
|
var savedSelection = selection && {
|
|
anchorNode: selection.anchorNode,
|
|
anchorOffset: selection.anchorOffset,
|
|
focusNode: selection.focusNode,
|
|
focusOffset: selection.focusOffset
|
|
};
|
|
view.scrollDOM.remove();
|
|
parent.insertBefore(view.scrollDOM, sibling);
|
|
try {
|
|
if (savedSelection && selection) {
|
|
selection.setPosition(savedSelection.anchorNode, savedSelection.anchorOffset);
|
|
if (savedSelection.focusNode) {
|
|
selection.extend(savedSelection.focusNode, savedSelection.focusOffset);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
view.focus();
|
|
view.contentDOM.dispatchEvent(new CustomEvent("compositionend"));
|
|
}
|
|
var matchMark = /* @__PURE__ */ import_view.Decoration.mark({ class: "cm-searchMatch" });
|
|
var showVimPanel = /* @__PURE__ */ import_state.StateEffect.define();
|
|
var vimPanelState = /* @__PURE__ */ import_state.StateField.define({
|
|
create: () => false,
|
|
update(value, tr) {
|
|
for (let e of tr.effects)
|
|
if (e.is(showVimPanel))
|
|
value = e.value;
|
|
return value;
|
|
},
|
|
provide: (f) => {
|
|
return import_view.showPanel.from(f, (on2) => on2 ? createVimPanel : null);
|
|
}
|
|
});
|
|
function createVimPanel(view) {
|
|
let dom = document.createElement("div");
|
|
dom.className = "cm-vim-panel";
|
|
let cm = view.cm;
|
|
if (cm.state.dialog) {
|
|
dom.appendChild(cm.state.dialog);
|
|
}
|
|
return { top: false, dom };
|
|
}
|
|
function statusPanel(view) {
|
|
let dom = document.createElement("div");
|
|
dom.className = "cm-vim-panel";
|
|
let cm = view.cm;
|
|
cm.state.statusbar = dom;
|
|
cm.state.vimPlugin.updateStatus();
|
|
return { dom };
|
|
}
|
|
function vim(options = {}) {
|
|
return [
|
|
vimStyle,
|
|
vimPlugin,
|
|
hideNativeSelection,
|
|
options.status ? import_view.showPanel.of(statusPanel) : vimPanelState
|
|
];
|
|
}
|
|
function getCM(view) {
|
|
return view.cm || null;
|
|
}
|
|
|
|
// src/CssFile.ts
|
|
var CssFile = class {
|
|
constructor(name) {
|
|
/** File extension. */
|
|
this.extension = "css";
|
|
if (typeof name !== "string" || name.length === 0) {
|
|
throw new Error("Invalid file name.");
|
|
}
|
|
const extensionWithDot = `.${this.extension}`;
|
|
const basename = name.endsWith(extensionWithDot) ? name.slice(0, name.length - extensionWithDot.length) : name;
|
|
this.name = `${basename}.${this.extension}`;
|
|
this.basename = basename;
|
|
}
|
|
};
|
|
|
|
// src/obsidian/file-system-helpers.ts
|
|
var import_obsidian = require("obsidian");
|
|
function getSnippetDirectory(app) {
|
|
return `${app.vault.configDir}/snippets/`;
|
|
}
|
|
async function readSnippetFile(app, file) {
|
|
const data = await app.vault.adapter.read(
|
|
(0, import_obsidian.normalizePath)(`${getSnippetDirectory(app)}${file.name}`)
|
|
);
|
|
return data;
|
|
}
|
|
async function createSnippetFile(app, fileName, data = "") {
|
|
const file = new CssFile(fileName);
|
|
await _validateFile(app, file);
|
|
await _createSnippetDirectoryIfNotExists(app);
|
|
await app.vault.adapter.write(
|
|
(0, import_obsidian.normalizePath)(`${getSnippetDirectory(app)}${file.name}`),
|
|
data
|
|
);
|
|
return file;
|
|
}
|
|
async function renameSnippetFile(app, oldFile, newFileName) {
|
|
const newFile = new CssFile(newFileName);
|
|
if (oldFile.name === newFile.name) return oldFile;
|
|
await _validateFile(app, newFile);
|
|
await app.vault.adapter.rename(
|
|
(0, import_obsidian.normalizePath)(`${getSnippetDirectory(app)}${oldFile.name}`),
|
|
(0, import_obsidian.normalizePath)(`${getSnippetDirectory(app)}${newFile.name}`)
|
|
);
|
|
toggleSnippetFileState(app, oldFile);
|
|
toggleSnippetFileState(app, newFile);
|
|
app.workspace.trigger("css-snippet-rename", newFile, oldFile.name);
|
|
return newFile;
|
|
}
|
|
async function writeSnippetFile(app, file, data) {
|
|
await app.vault.adapter.write(
|
|
(0, import_obsidian.normalizePath)(`${getSnippetDirectory(app)}${file.name}`),
|
|
data
|
|
);
|
|
}
|
|
async function checkSnippetExists(app, fileName) {
|
|
return app.vault.adapter.exists(
|
|
(0, import_obsidian.normalizePath)(`${getSnippetDirectory(app)}${fileName}`)
|
|
);
|
|
}
|
|
async function deleteSnippetFile(app, file) {
|
|
await app.vault.adapter.remove(
|
|
(0, import_obsidian.normalizePath)(`${getSnippetDirectory(app)}${file.name}`)
|
|
);
|
|
}
|
|
function toggleSnippetFileState(app, file) {
|
|
var _a;
|
|
if (!((_a = app.customCss) == null ? void 0 : _a.enabledSnippets) || !app.customCss.setCssEnabledStatus) {
|
|
throw new Error("Failed to enable/disable CSS snippet.");
|
|
}
|
|
const isEnabled = app.customCss.enabledSnippets.has(file.basename);
|
|
app.customCss.setCssEnabledStatus(file.basename, !isEnabled);
|
|
return !isEnabled;
|
|
}
|
|
async function _createSnippetDirectoryIfNotExists(app) {
|
|
if (!await app.vault.adapter.exists(getSnippetDirectory(app))) {
|
|
await app.vault.adapter.mkdir(getSnippetDirectory(app));
|
|
}
|
|
}
|
|
async function _validateFile(app, file) {
|
|
const errors = {
|
|
exists: "",
|
|
regex: ""
|
|
};
|
|
if (file.name.length > 0 && await checkSnippetExists(app, file.name)) {
|
|
errors.exists = "File already exists.";
|
|
}
|
|
const regex = /^[0-9a-zA-Z\-_ ]+\.css$/;
|
|
if (!regex.test(file.name)) {
|
|
errors.regex = "Must end with .css and only contain alphanumeric, spaces, dashes, or underscore characters.";
|
|
}
|
|
if (Object.values(errors).some((x) => x !== "")) {
|
|
const message = Object.values(errors).filter((x) => x !== "").reduce((acc, curr) => `${acc}
|
|
${curr}`, "Failed to create file.");
|
|
throw new Error(message);
|
|
}
|
|
}
|
|
|
|
// src/codemirror-extensions/basic-extensions.ts
|
|
var import_commands2 = require("@codemirror/commands");
|
|
var import_language6 = require("@codemirror/language");
|
|
var import_state3 = require("@codemirror/state");
|
|
var import_view5 = require("@codemirror/view");
|
|
|
|
// node_modules/@codemirror/autocomplete/dist/index.js
|
|
var import_state2 = require("@codemirror/state");
|
|
var import_view2 = require("@codemirror/view");
|
|
var import_language2 = require("@codemirror/language");
|
|
var CompletionContext = class {
|
|
/**
|
|
Create a new completion context. (Mostly useful for testing
|
|
completion sources—in the editor, the extension will create
|
|
these for you.)
|
|
*/
|
|
constructor(state, pos, explicit) {
|
|
this.state = state;
|
|
this.pos = pos;
|
|
this.explicit = explicit;
|
|
this.abortListeners = [];
|
|
}
|
|
/**
|
|
Get the extent, content, and (if there is a token) type of the
|
|
token before `this.pos`.
|
|
*/
|
|
tokenBefore(types) {
|
|
let token = (0, import_language2.syntaxTree)(this.state).resolveInner(this.pos, -1);
|
|
while (token && types.indexOf(token.name) < 0)
|
|
token = token.parent;
|
|
return token ? {
|
|
from: token.from,
|
|
to: this.pos,
|
|
text: this.state.sliceDoc(token.from, this.pos),
|
|
type: token.type
|
|
} : null;
|
|
}
|
|
/**
|
|
Get the match of the given expression directly before the
|
|
cursor.
|
|
*/
|
|
matchBefore(expr) {
|
|
let line = this.state.doc.lineAt(this.pos);
|
|
let start = Math.max(line.from, this.pos - 250);
|
|
let str = line.text.slice(start - line.from, this.pos - line.from);
|
|
let found = str.search(ensureAnchor(expr, false));
|
|
return found < 0 ? null : { from: start + found, to: this.pos, text: str.slice(found) };
|
|
}
|
|
/**
|
|
Yields true when the query has been aborted. Can be useful in
|
|
asynchronous queries to avoid doing work that will be ignored.
|
|
*/
|
|
get aborted() {
|
|
return this.abortListeners == null;
|
|
}
|
|
/**
|
|
Allows you to register abort handlers, which will be called when
|
|
the query is
|
|
[aborted](https://codemirror.net/6/docs/ref/#autocomplete.CompletionContext.aborted).
|
|
*/
|
|
addEventListener(type, listener) {
|
|
if (type == "abort" && this.abortListeners)
|
|
this.abortListeners.push(listener);
|
|
}
|
|
};
|
|
function toSet(chars) {
|
|
let flat = Object.keys(chars).join("");
|
|
let words = /\w/.test(flat);
|
|
if (words)
|
|
flat = flat.replace(/\w/g, "");
|
|
return `[${words ? "\\w" : ""}${flat.replace(/[^\w\s]/g, "\\$&")}]`;
|
|
}
|
|
function prefixMatch(options) {
|
|
let first = /* @__PURE__ */ Object.create(null), rest = /* @__PURE__ */ Object.create(null);
|
|
for (let { label } of options) {
|
|
first[label[0]] = true;
|
|
for (let i = 1; i < label.length; i++)
|
|
rest[label[i]] = true;
|
|
}
|
|
let source = toSet(first) + toSet(rest) + "*$";
|
|
return [new RegExp("^" + source), new RegExp(source)];
|
|
}
|
|
function completeFromList(list) {
|
|
let options = list.map((o) => typeof o == "string" ? { label: o } : o);
|
|
let [validFor, match] = options.every((o) => /^\w+$/.test(o.label)) ? [/\w*$/, /\w+$/] : prefixMatch(options);
|
|
return (context) => {
|
|
let token = context.matchBefore(match);
|
|
return token || context.explicit ? { from: token ? token.from : context.pos, options, validFor } : null;
|
|
};
|
|
}
|
|
var Option = class {
|
|
constructor(completion, source, match, score2) {
|
|
this.completion = completion;
|
|
this.source = source;
|
|
this.match = match;
|
|
this.score = score2;
|
|
}
|
|
};
|
|
function cur(state) {
|
|
return state.selection.main.from;
|
|
}
|
|
function ensureAnchor(expr, start) {
|
|
var _a;
|
|
let { source } = expr;
|
|
let addStart = start && source[0] != "^", addEnd = source[source.length - 1] != "$";
|
|
if (!addStart && !addEnd)
|
|
return expr;
|
|
return new RegExp(`${addStart ? "^" : ""}(?:${source})${addEnd ? "$" : ""}`, (_a = expr.flags) !== null && _a !== void 0 ? _a : expr.ignoreCase ? "i" : "");
|
|
}
|
|
var pickedCompletion = /* @__PURE__ */ import_state2.Annotation.define();
|
|
function insertCompletionText(state, text, from, to) {
|
|
let { main } = state.selection, fromOff = from - main.from, toOff = to - main.from;
|
|
return Object.assign(Object.assign({}, state.changeByRange((range) => {
|
|
if (range != main && from != to && state.sliceDoc(range.from + fromOff, range.from + toOff) != state.sliceDoc(from, to))
|
|
return { range };
|
|
return {
|
|
changes: { from: range.from + fromOff, to: to == main.from ? range.to : range.from + toOff, insert: text },
|
|
range: import_state2.EditorSelection.cursor(range.from + fromOff + text.length)
|
|
};
|
|
})), { userEvent: "input.complete" });
|
|
}
|
|
var SourceCache = /* @__PURE__ */ new WeakMap();
|
|
function asSource(source) {
|
|
if (!Array.isArray(source))
|
|
return source;
|
|
let known = SourceCache.get(source);
|
|
if (!known)
|
|
SourceCache.set(source, known = completeFromList(source));
|
|
return known;
|
|
}
|
|
var startCompletionEffect = /* @__PURE__ */ import_state2.StateEffect.define();
|
|
var closeCompletionEffect = /* @__PURE__ */ import_state2.StateEffect.define();
|
|
var FuzzyMatcher = class {
|
|
constructor(pattern) {
|
|
this.pattern = pattern;
|
|
this.chars = [];
|
|
this.folded = [];
|
|
this.any = [];
|
|
this.precise = [];
|
|
this.byWord = [];
|
|
for (let p = 0; p < pattern.length; ) {
|
|
let char = (0, import_state2.codePointAt)(pattern, p), size = (0, import_state2.codePointSize)(char);
|
|
this.chars.push(char);
|
|
let part = pattern.slice(p, p + size), upper = part.toUpperCase();
|
|
this.folded.push((0, import_state2.codePointAt)(upper == part ? part.toLowerCase() : upper, 0));
|
|
p += size;
|
|
}
|
|
this.astral = pattern.length != this.chars.length;
|
|
}
|
|
// Matches a given word (completion) against the pattern (input).
|
|
// Will return null for no match, and otherwise an array that starts
|
|
// with the match score, followed by any number of `from, to` pairs
|
|
// indicating the matched parts of `word`.
|
|
//
|
|
// The score is a number that is more negative the worse the match
|
|
// is. See `Penalty` above.
|
|
match(word) {
|
|
if (this.pattern.length == 0)
|
|
return [
|
|
-100
|
|
/* NotFull */
|
|
];
|
|
if (word.length < this.pattern.length)
|
|
return null;
|
|
let { chars, folded, any, precise, byWord } = this;
|
|
if (chars.length == 1) {
|
|
let first = (0, import_state2.codePointAt)(word, 0), firstSize = (0, import_state2.codePointSize)(first);
|
|
let score2 = firstSize == word.length ? 0 : -100;
|
|
if (first == chars[0]) ;
|
|
else if (first == folded[0])
|
|
score2 += -200;
|
|
else
|
|
return null;
|
|
return [score2, 0, firstSize];
|
|
}
|
|
let direct = word.indexOf(this.pattern);
|
|
if (direct == 0)
|
|
return [word.length == this.pattern.length ? 0 : -100, 0, this.pattern.length];
|
|
let len = chars.length, anyTo = 0;
|
|
if (direct < 0) {
|
|
for (let i = 0, e = Math.min(word.length, 200); i < e && anyTo < len; ) {
|
|
let next = (0, import_state2.codePointAt)(word, i);
|
|
if (next == chars[anyTo] || next == folded[anyTo])
|
|
any[anyTo++] = i;
|
|
i += (0, import_state2.codePointSize)(next);
|
|
}
|
|
if (anyTo < len)
|
|
return null;
|
|
}
|
|
let preciseTo = 0;
|
|
let byWordTo = 0, byWordFolded = false;
|
|
let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
|
|
let hasLower = /[a-z]/.test(word), wordAdjacent = true;
|
|
for (let i = 0, e = Math.min(word.length, 200), prevType = 0; i < e && byWordTo < len; ) {
|
|
let next = (0, import_state2.codePointAt)(word, i);
|
|
if (direct < 0) {
|
|
if (preciseTo < len && next == chars[preciseTo])
|
|
precise[preciseTo++] = i;
|
|
if (adjacentTo < len) {
|
|
if (next == chars[adjacentTo] || next == folded[adjacentTo]) {
|
|
if (adjacentTo == 0)
|
|
adjacentStart = i;
|
|
adjacentEnd = i + 1;
|
|
adjacentTo++;
|
|
} else {
|
|
adjacentTo = 0;
|
|
}
|
|
}
|
|
}
|
|
let ch, type = next < 255 ? next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 : next >= 65 && next <= 90 ? 1 : 0 : (ch = (0, import_state2.fromCodePoint)(next)) != ch.toLowerCase() ? 1 : ch != ch.toUpperCase() ? 2 : 0;
|
|
if (!i || type == 1 && hasLower || prevType == 0 && type != 0) {
|
|
if (chars[byWordTo] == next || folded[byWordTo] == next && (byWordFolded = true))
|
|
byWord[byWordTo++] = i;
|
|
else if (byWord.length)
|
|
wordAdjacent = false;
|
|
}
|
|
prevType = type;
|
|
i += (0, import_state2.codePointSize)(next);
|
|
}
|
|
if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
|
|
return this.result(-100 + (byWordFolded ? -200 : 0), byWord, word);
|
|
if (adjacentTo == len && adjacentStart == 0)
|
|
return [-200 - word.length + (adjacentEnd == word.length ? 0 : -100), 0, adjacentEnd];
|
|
if (direct > -1)
|
|
return [-700 - word.length, direct, direct + this.pattern.length];
|
|
if (adjacentTo == len)
|
|
return [-200 + -700 - word.length, adjacentStart, adjacentEnd];
|
|
if (byWordTo == len)
|
|
return this.result(-100 + (byWordFolded ? -200 : 0) + -700 + (wordAdjacent ? 0 : -1100), byWord, word);
|
|
return chars.length == 2 ? null : this.result((any[0] ? -700 : 0) + -200 + -1100, any, word);
|
|
}
|
|
result(score2, positions, word) {
|
|
let result = [score2 - word.length], i = 1;
|
|
for (let pos of positions) {
|
|
let to = pos + (this.astral ? (0, import_state2.codePointSize)((0, import_state2.codePointAt)(word, pos)) : 1);
|
|
if (i > 1 && result[i - 1] == pos)
|
|
result[i - 1] = to;
|
|
else {
|
|
result[i++] = pos;
|
|
result[i++] = to;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
var completionConfig = /* @__PURE__ */ import_state2.Facet.define({
|
|
combine(configs) {
|
|
return (0, import_state2.combineConfig)(configs, {
|
|
activateOnTyping: true,
|
|
selectOnOpen: true,
|
|
override: null,
|
|
closeOnBlur: true,
|
|
maxRenderedOptions: 100,
|
|
defaultKeymap: true,
|
|
tooltipClass: () => "",
|
|
optionClass: () => "",
|
|
aboveCursor: false,
|
|
icons: true,
|
|
addToOptions: [],
|
|
positionInfo: defaultPositionInfo,
|
|
compareCompletions: (a, b) => a.label.localeCompare(b.label),
|
|
interactionDelay: 75
|
|
}, {
|
|
defaultKeymap: (a, b) => a && b,
|
|
closeOnBlur: (a, b) => a && b,
|
|
icons: (a, b) => a && b,
|
|
tooltipClass: (a, b) => (c) => joinClass(a(c), b(c)),
|
|
optionClass: (a, b) => (c) => joinClass(a(c), b(c)),
|
|
addToOptions: (a, b) => a.concat(b)
|
|
});
|
|
}
|
|
});
|
|
function joinClass(a, b) {
|
|
return a ? b ? a + " " + b : a : b;
|
|
}
|
|
function defaultPositionInfo(view, list, option, info, space2) {
|
|
let rtl = view.textDirection == import_view2.Direction.RTL, left = rtl, narrow = false;
|
|
let side = "top", offset, maxWidth;
|
|
let spaceLeft = list.left - space2.left, spaceRight = space2.right - list.right;
|
|
let infoWidth = info.right - info.left, infoHeight = info.bottom - info.top;
|
|
if (left && spaceLeft < Math.min(infoWidth, spaceRight))
|
|
left = false;
|
|
else if (!left && spaceRight < Math.min(infoWidth, spaceLeft))
|
|
left = true;
|
|
if (infoWidth <= (left ? spaceLeft : spaceRight)) {
|
|
offset = Math.max(space2.top, Math.min(option.top, space2.bottom - infoHeight)) - list.top;
|
|
maxWidth = Math.min(400, left ? spaceLeft : spaceRight);
|
|
} else {
|
|
narrow = true;
|
|
maxWidth = Math.min(
|
|
400,
|
|
(rtl ? list.right : space2.right - list.left) - 30
|
|
/* Margin */
|
|
);
|
|
let spaceBelow = space2.bottom - list.bottom;
|
|
if (spaceBelow >= infoHeight || spaceBelow > list.top) {
|
|
offset = option.bottom - list.top;
|
|
} else {
|
|
side = "bottom";
|
|
offset = list.bottom - option.top;
|
|
}
|
|
}
|
|
return {
|
|
style: `${side}: ${offset}px; max-width: ${maxWidth}px`,
|
|
class: "cm-completionInfo-" + (narrow ? rtl ? "left-narrow" : "right-narrow" : left ? "left" : "right")
|
|
};
|
|
}
|
|
function optionContent(config3) {
|
|
let content = config3.addToOptions.slice();
|
|
if (config3.icons)
|
|
content.push({
|
|
render(completion) {
|
|
let icon = document.createElement("div");
|
|
icon.classList.add("cm-completionIcon");
|
|
if (completion.type)
|
|
icon.classList.add(...completion.type.split(/\s+/g).map((cls) => "cm-completionIcon-" + cls));
|
|
icon.setAttribute("aria-hidden", "true");
|
|
return icon;
|
|
},
|
|
position: 20
|
|
});
|
|
content.push({
|
|
render(completion, _s, match) {
|
|
let labelElt = document.createElement("span");
|
|
labelElt.className = "cm-completionLabel";
|
|
let { label } = completion, off2 = 0;
|
|
for (let j = 1; j < match.length; ) {
|
|
let from = match[j++], to = match[j++];
|
|
if (from > off2)
|
|
labelElt.appendChild(document.createTextNode(label.slice(off2, from)));
|
|
let span = labelElt.appendChild(document.createElement("span"));
|
|
span.appendChild(document.createTextNode(label.slice(from, to)));
|
|
span.className = "cm-completionMatchedText";
|
|
off2 = to;
|
|
}
|
|
if (off2 < label.length)
|
|
labelElt.appendChild(document.createTextNode(label.slice(off2)));
|
|
return labelElt;
|
|
},
|
|
position: 50
|
|
}, {
|
|
render(completion) {
|
|
if (!completion.detail)
|
|
return null;
|
|
let detailElt = document.createElement("span");
|
|
detailElt.className = "cm-completionDetail";
|
|
detailElt.textContent = completion.detail;
|
|
return detailElt;
|
|
},
|
|
position: 80
|
|
});
|
|
return content.sort((a, b) => a.position - b.position).map((a) => a.render);
|
|
}
|
|
function rangeAroundSelected(total, selected, max) {
|
|
if (total <= max)
|
|
return { from: 0, to: total };
|
|
if (selected < 0)
|
|
selected = 0;
|
|
if (selected <= total >> 1) {
|
|
let off3 = Math.floor(selected / max);
|
|
return { from: off3 * max, to: (off3 + 1) * max };
|
|
}
|
|
let off2 = Math.floor((total - selected) / max);
|
|
return { from: total - (off2 + 1) * max, to: total - off2 * max };
|
|
}
|
|
var CompletionTooltip = class {
|
|
constructor(view, stateField, applyCompletion2) {
|
|
this.view = view;
|
|
this.stateField = stateField;
|
|
this.applyCompletion = applyCompletion2;
|
|
this.info = null;
|
|
this.infoDestroy = null;
|
|
this.placeInfoReq = {
|
|
read: () => this.measureInfo(),
|
|
write: (pos) => this.placeInfo(pos),
|
|
key: this
|
|
};
|
|
this.space = null;
|
|
this.currentClass = "";
|
|
let cState = view.state.field(stateField);
|
|
let { options, selected } = cState.open;
|
|
let config3 = view.state.facet(completionConfig);
|
|
this.optionContent = optionContent(config3);
|
|
this.optionClass = config3.optionClass;
|
|
this.tooltipClass = config3.tooltipClass;
|
|
this.range = rangeAroundSelected(options.length, selected, config3.maxRenderedOptions);
|
|
this.dom = document.createElement("div");
|
|
this.dom.className = "cm-tooltip-autocomplete";
|
|
this.updateTooltipClass(view.state);
|
|
this.dom.addEventListener("mousedown", (e) => {
|
|
for (let dom = e.target, match; dom && dom != this.dom; dom = dom.parentNode) {
|
|
if (dom.nodeName == "LI" && (match = /-(\d+)$/.exec(dom.id)) && +match[1] < options.length) {
|
|
this.applyCompletion(view, options[+match[1]]);
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
this.dom.addEventListener("focusout", (e) => {
|
|
let state = view.state.field(this.stateField, false);
|
|
if (state && state.tooltip && view.state.facet(completionConfig).closeOnBlur && e.relatedTarget != view.contentDOM)
|
|
view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
});
|
|
this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
|
|
this.list.addEventListener("scroll", () => {
|
|
if (this.info)
|
|
this.view.requestMeasure(this.placeInfoReq);
|
|
});
|
|
}
|
|
mount() {
|
|
this.updateSel();
|
|
}
|
|
update(update) {
|
|
var _a, _b, _c;
|
|
let cState = update.state.field(this.stateField);
|
|
let prevState = update.startState.field(this.stateField);
|
|
this.updateTooltipClass(update.state);
|
|
if (cState != prevState) {
|
|
this.updateSel();
|
|
if (((_a = cState.open) === null || _a === void 0 ? void 0 : _a.disabled) != ((_b = prevState.open) === null || _b === void 0 ? void 0 : _b.disabled))
|
|
this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!((_c = cState.open) === null || _c === void 0 ? void 0 : _c.disabled));
|
|
}
|
|
}
|
|
updateTooltipClass(state) {
|
|
let cls = this.tooltipClass(state);
|
|
if (cls != this.currentClass) {
|
|
for (let c of this.currentClass.split(" "))
|
|
if (c)
|
|
this.dom.classList.remove(c);
|
|
for (let c of cls.split(" "))
|
|
if (c)
|
|
this.dom.classList.add(c);
|
|
this.currentClass = cls;
|
|
}
|
|
}
|
|
positioned(space2) {
|
|
this.space = space2;
|
|
if (this.info)
|
|
this.view.requestMeasure(this.placeInfoReq);
|
|
}
|
|
updateSel() {
|
|
let cState = this.view.state.field(this.stateField), open = cState.open;
|
|
if (open.selected > -1 && open.selected < this.range.from || open.selected >= this.range.to) {
|
|
this.range = rangeAroundSelected(open.options.length, open.selected, this.view.state.facet(completionConfig).maxRenderedOptions);
|
|
this.list.remove();
|
|
this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
|
|
this.list.addEventListener("scroll", () => {
|
|
if (this.info)
|
|
this.view.requestMeasure(this.placeInfoReq);
|
|
});
|
|
}
|
|
if (this.updateSelectedOption(open.selected)) {
|
|
this.destroyInfo();
|
|
let { completion } = open.options[open.selected];
|
|
let { info } = completion;
|
|
if (!info)
|
|
return;
|
|
let infoResult = typeof info === "string" ? document.createTextNode(info) : info(completion);
|
|
if (!infoResult)
|
|
return;
|
|
if ("then" in infoResult) {
|
|
infoResult.then((obj) => {
|
|
if (obj && this.view.state.field(this.stateField, false) == cState)
|
|
this.addInfoPane(obj, completion);
|
|
}).catch((e) => (0, import_view2.logException)(this.view.state, e, "completion info"));
|
|
} else {
|
|
this.addInfoPane(infoResult, completion);
|
|
}
|
|
}
|
|
}
|
|
addInfoPane(content, completion) {
|
|
this.destroyInfo();
|
|
let wrap = this.info = document.createElement("div");
|
|
wrap.className = "cm-tooltip cm-completionInfo";
|
|
if (content.nodeType != null) {
|
|
wrap.appendChild(content);
|
|
this.infoDestroy = null;
|
|
} else {
|
|
let { dom, destroy } = content;
|
|
wrap.appendChild(dom);
|
|
this.infoDestroy = destroy || null;
|
|
}
|
|
this.dom.appendChild(wrap);
|
|
this.view.requestMeasure(this.placeInfoReq);
|
|
}
|
|
updateSelectedOption(selected) {
|
|
let set = null;
|
|
for (let opt = this.list.firstChild, i = this.range.from; opt; opt = opt.nextSibling, i++) {
|
|
if (opt.nodeName != "LI" || !opt.id) {
|
|
i--;
|
|
} else if (i == selected) {
|
|
if (!opt.hasAttribute("aria-selected")) {
|
|
opt.setAttribute("aria-selected", "true");
|
|
set = opt;
|
|
}
|
|
} else {
|
|
if (opt.hasAttribute("aria-selected"))
|
|
opt.removeAttribute("aria-selected");
|
|
}
|
|
}
|
|
if (set)
|
|
scrollIntoView(this.list, set);
|
|
return set;
|
|
}
|
|
measureInfo() {
|
|
let sel = this.dom.querySelector("[aria-selected]");
|
|
if (!sel || !this.info)
|
|
return null;
|
|
let listRect = this.dom.getBoundingClientRect();
|
|
let infoRect = this.info.getBoundingClientRect();
|
|
let selRect = sel.getBoundingClientRect();
|
|
let space2 = this.space;
|
|
if (!space2) {
|
|
let win = this.dom.ownerDocument.defaultView || window;
|
|
space2 = { left: 0, top: 0, right: win.innerWidth, bottom: win.innerHeight };
|
|
}
|
|
if (selRect.top > Math.min(space2.bottom, listRect.bottom) - 10 || selRect.bottom < Math.max(space2.top, listRect.top) + 10)
|
|
return null;
|
|
return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space2);
|
|
}
|
|
placeInfo(pos) {
|
|
if (this.info) {
|
|
if (pos) {
|
|
if (pos.style)
|
|
this.info.style.cssText = pos.style;
|
|
this.info.className = "cm-tooltip cm-completionInfo " + (pos.class || "");
|
|
} else {
|
|
this.info.style.cssText = "top: -1e6px";
|
|
}
|
|
}
|
|
}
|
|
createListBox(options, id, range) {
|
|
const ul = document.createElement("ul");
|
|
ul.id = id;
|
|
ul.setAttribute("role", "listbox");
|
|
ul.setAttribute("aria-expanded", "true");
|
|
ul.setAttribute("aria-label", this.view.state.phrase("Completions"));
|
|
let curSection = null;
|
|
for (let i = range.from; i < range.to; i++) {
|
|
let { completion, match } = options[i], { section } = completion;
|
|
if (section) {
|
|
let name = typeof section == "string" ? section : section.name;
|
|
if (name != curSection && (i > range.from || range.from == 0)) {
|
|
curSection = name;
|
|
if (typeof section != "string" && section.header) {
|
|
ul.appendChild(section.header(section));
|
|
} else {
|
|
let header = ul.appendChild(document.createElement("completion-section"));
|
|
header.textContent = name;
|
|
}
|
|
}
|
|
}
|
|
const li = ul.appendChild(document.createElement("li"));
|
|
li.id = id + "-" + i;
|
|
li.setAttribute("role", "option");
|
|
let cls = this.optionClass(completion);
|
|
if (cls)
|
|
li.className = cls;
|
|
for (let source of this.optionContent) {
|
|
let node = source(completion, this.view.state, match);
|
|
if (node)
|
|
li.appendChild(node);
|
|
}
|
|
}
|
|
if (range.from)
|
|
ul.classList.add("cm-completionListIncompleteTop");
|
|
if (range.to < options.length)
|
|
ul.classList.add("cm-completionListIncompleteBottom");
|
|
return ul;
|
|
}
|
|
destroyInfo() {
|
|
if (this.info) {
|
|
if (this.infoDestroy)
|
|
this.infoDestroy();
|
|
this.info.remove();
|
|
this.info = null;
|
|
}
|
|
}
|
|
destroy() {
|
|
this.destroyInfo();
|
|
}
|
|
};
|
|
function completionTooltip(stateField, applyCompletion2) {
|
|
return (view) => new CompletionTooltip(view, stateField, applyCompletion2);
|
|
}
|
|
function scrollIntoView(container, element) {
|
|
let parent = container.getBoundingClientRect();
|
|
let self = element.getBoundingClientRect();
|
|
if (self.top < parent.top)
|
|
container.scrollTop -= parent.top - self.top;
|
|
else if (self.bottom > parent.bottom)
|
|
container.scrollTop += self.bottom - parent.bottom;
|
|
}
|
|
function score(option) {
|
|
return (option.boost || 0) * 100 + (option.apply ? 10 : 0) + (option.info ? 5 : 0) + (option.type ? 1 : 0);
|
|
}
|
|
function sortOptions(active, state) {
|
|
let options = [];
|
|
let sections = null;
|
|
let addOption = (option) => {
|
|
options.push(option);
|
|
let { section } = option.completion;
|
|
if (section) {
|
|
if (!sections)
|
|
sections = [];
|
|
let name = typeof section == "string" ? section : section.name;
|
|
if (!sections.some((s) => s.name == name))
|
|
sections.push(typeof section == "string" ? { name } : section);
|
|
}
|
|
};
|
|
for (let a of active)
|
|
if (a.hasResult()) {
|
|
if (a.result.filter === false) {
|
|
let getMatch = a.result.getMatch;
|
|
for (let option of a.result.options) {
|
|
let match = [1e9 - options.length];
|
|
if (getMatch)
|
|
for (let n of getMatch(option))
|
|
match.push(n);
|
|
addOption(new Option(option, a.source, match, match[0]));
|
|
}
|
|
} else {
|
|
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
|
|
for (let option of a.result.options)
|
|
if (match = matcher.match(option.label)) {
|
|
addOption(new Option(option, a.source, match, match[0] + (option.boost || 0)));
|
|
}
|
|
}
|
|
}
|
|
if (sections) {
|
|
let sectionOrder = /* @__PURE__ */ Object.create(null), pos = 0;
|
|
let cmp = (a, b) => {
|
|
var _a, _b;
|
|
return ((_a = a.rank) !== null && _a !== void 0 ? _a : 1e9) - ((_b = b.rank) !== null && _b !== void 0 ? _b : 1e9) || (a.name < b.name ? -1 : 1);
|
|
};
|
|
for (let s of sections.sort(cmp)) {
|
|
pos -= 1e5;
|
|
sectionOrder[s.name] = pos;
|
|
}
|
|
for (let option of options) {
|
|
let { section } = option.completion;
|
|
if (section)
|
|
option.score += sectionOrder[typeof section == "string" ? section : section.name];
|
|
}
|
|
}
|
|
let result = [], prev = null;
|
|
let compare = state.facet(completionConfig).compareCompletions;
|
|
for (let opt of options.sort((a, b) => b.score - a.score || compare(a.completion, b.completion))) {
|
|
let cur2 = opt.completion;
|
|
if (!prev || prev.label != cur2.label || prev.detail != cur2.detail || prev.type != null && cur2.type != null && prev.type != cur2.type || prev.apply != cur2.apply || prev.boost != cur2.boost)
|
|
result.push(opt);
|
|
else if (score(opt.completion) > score(prev))
|
|
result[result.length - 1] = opt;
|
|
prev = opt.completion;
|
|
}
|
|
return result;
|
|
}
|
|
var CompletionDialog = class _CompletionDialog {
|
|
constructor(options, attrs, tooltip, timestamp, selected, disabled) {
|
|
this.options = options;
|
|
this.attrs = attrs;
|
|
this.tooltip = tooltip;
|
|
this.timestamp = timestamp;
|
|
this.selected = selected;
|
|
this.disabled = disabled;
|
|
}
|
|
setSelected(selected, id) {
|
|
return selected == this.selected || selected >= this.options.length ? this : new _CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected, this.disabled);
|
|
}
|
|
static build(active, state, id, prev, conf) {
|
|
let options = sortOptions(active, state);
|
|
if (!options.length) {
|
|
return prev && active.some(
|
|
(a) => a.state == 1
|
|
/* Pending */
|
|
) ? new _CompletionDialog(prev.options, prev.attrs, prev.tooltip, prev.timestamp, prev.selected, true) : null;
|
|
}
|
|
let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
|
|
if (prev && prev.selected != selected && prev.selected != -1) {
|
|
let selectedValue = prev.options[prev.selected].completion;
|
|
for (let i = 0; i < options.length; i++)
|
|
if (options[i].completion == selectedValue) {
|
|
selected = i;
|
|
break;
|
|
}
|
|
}
|
|
return new _CompletionDialog(options, makeAttrs(id, selected), {
|
|
pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
|
|
create: completionTooltip(completionState, applyCompletion),
|
|
above: conf.aboveCursor
|
|
}, prev ? prev.timestamp : Date.now(), selected, false);
|
|
}
|
|
map(changes) {
|
|
return new _CompletionDialog(this.options, this.attrs, Object.assign(Object.assign({}, this.tooltip), { pos: changes.mapPos(this.tooltip.pos) }), this.timestamp, this.selected, this.disabled);
|
|
}
|
|
};
|
|
var CompletionState = class _CompletionState {
|
|
constructor(active, id, open) {
|
|
this.active = active;
|
|
this.id = id;
|
|
this.open = open;
|
|
}
|
|
static start() {
|
|
return new _CompletionState(none, "cm-ac-" + Math.floor(Math.random() * 2e6).toString(36), null);
|
|
}
|
|
update(tr) {
|
|
let { state } = tr, conf = state.facet(completionConfig);
|
|
let sources = conf.override || state.languageDataAt("autocomplete", cur(state)).map(asSource);
|
|
let active = sources.map((source) => {
|
|
let value = this.active.find((s) => s.source == source) || new ActiveSource(
|
|
source,
|
|
this.active.some(
|
|
(a) => a.state != 0
|
|
/* Inactive */
|
|
) ? 1 : 0
|
|
/* Inactive */
|
|
);
|
|
return value.update(tr, conf);
|
|
});
|
|
if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
|
|
active = this.active;
|
|
let open = this.open;
|
|
if (open && tr.docChanged)
|
|
open = open.map(tr.changes);
|
|
if (tr.selection || active.some((a) => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) || !sameResults(active, this.active))
|
|
open = CompletionDialog.build(active, state, this.id, open, conf);
|
|
else if (open && open.disabled && !active.some(
|
|
(a) => a.state == 1
|
|
/* Pending */
|
|
))
|
|
open = null;
|
|
if (!open && active.every(
|
|
(a) => a.state != 1
|
|
/* Pending */
|
|
) && active.some((a) => a.hasResult()))
|
|
active = active.map((a) => a.hasResult() ? new ActiveSource(
|
|
a.source,
|
|
0
|
|
/* Inactive */
|
|
) : a);
|
|
for (let effect of tr.effects)
|
|
if (effect.is(setSelectedEffect))
|
|
open = open && open.setSelected(effect.value, this.id);
|
|
return active == this.active && open == this.open ? this : new _CompletionState(active, this.id, open);
|
|
}
|
|
get tooltip() {
|
|
return this.open ? this.open.tooltip : null;
|
|
}
|
|
get attrs() {
|
|
return this.open ? this.open.attrs : baseAttrs;
|
|
}
|
|
};
|
|
function sameResults(a, b) {
|
|
if (a == b)
|
|
return true;
|
|
for (let iA = 0, iB = 0; ; ) {
|
|
while (iA < a.length && !a[iA].hasResult)
|
|
iA++;
|
|
while (iB < b.length && !b[iB].hasResult)
|
|
iB++;
|
|
let endA = iA == a.length, endB = iB == b.length;
|
|
if (endA || endB)
|
|
return endA == endB;
|
|
if (a[iA++].result != b[iB++].result)
|
|
return false;
|
|
}
|
|
}
|
|
var baseAttrs = {
|
|
"aria-autocomplete": "list"
|
|
};
|
|
function makeAttrs(id, selected) {
|
|
let result = {
|
|
"aria-autocomplete": "list",
|
|
"aria-haspopup": "listbox",
|
|
"aria-controls": id
|
|
};
|
|
if (selected > -1)
|
|
result["aria-activedescendant"] = id + "-" + selected;
|
|
return result;
|
|
}
|
|
var none = [];
|
|
function getUserEvent(tr) {
|
|
return tr.isUserEvent("input.type") ? "input" : tr.isUserEvent("delete.backward") ? "delete" : null;
|
|
}
|
|
var ActiveSource = class _ActiveSource {
|
|
constructor(source, state, explicitPos = -1) {
|
|
this.source = source;
|
|
this.state = state;
|
|
this.explicitPos = explicitPos;
|
|
}
|
|
hasResult() {
|
|
return false;
|
|
}
|
|
update(tr, conf) {
|
|
let event = getUserEvent(tr), value = this;
|
|
if (event)
|
|
value = value.handleUserEvent(tr, event, conf);
|
|
else if (tr.docChanged)
|
|
value = value.handleChange(tr);
|
|
else if (tr.selection && value.state != 0)
|
|
value = new _ActiveSource(
|
|
value.source,
|
|
0
|
|
/* Inactive */
|
|
);
|
|
for (let effect of tr.effects) {
|
|
if (effect.is(startCompletionEffect))
|
|
value = new _ActiveSource(value.source, 1, effect.value ? cur(tr.state) : -1);
|
|
else if (effect.is(closeCompletionEffect))
|
|
value = new _ActiveSource(
|
|
value.source,
|
|
0
|
|
/* Inactive */
|
|
);
|
|
else if (effect.is(setActiveEffect)) {
|
|
for (let active of effect.value)
|
|
if (active.source == value.source)
|
|
value = active;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
handleUserEvent(tr, type, conf) {
|
|
return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new _ActiveSource(
|
|
this.source,
|
|
1
|
|
/* Pending */
|
|
);
|
|
}
|
|
handleChange(tr) {
|
|
return tr.changes.touchesRange(cur(tr.startState)) ? new _ActiveSource(
|
|
this.source,
|
|
0
|
|
/* Inactive */
|
|
) : this.map(tr.changes);
|
|
}
|
|
map(changes) {
|
|
return changes.empty || this.explicitPos < 0 ? this : new _ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
|
|
}
|
|
};
|
|
var ActiveResult = class _ActiveResult extends ActiveSource {
|
|
constructor(source, explicitPos, result, from, to) {
|
|
super(source, 2, explicitPos);
|
|
this.result = result;
|
|
this.from = from;
|
|
this.to = to;
|
|
}
|
|
hasResult() {
|
|
return true;
|
|
}
|
|
handleUserEvent(tr, type, conf) {
|
|
var _a;
|
|
let from = tr.changes.mapPos(this.from), to = tr.changes.mapPos(this.to, 1);
|
|
let pos = cur(tr.state);
|
|
if ((this.explicitPos < 0 ? pos <= from : pos < this.from) || pos > to || type == "delete" && cur(tr.startState) == this.from)
|
|
return new ActiveSource(
|
|
this.source,
|
|
type == "input" && conf.activateOnTyping ? 1 : 0
|
|
/* Inactive */
|
|
);
|
|
let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos), updated;
|
|
if (checkValid(this.result.validFor, tr.state, from, to))
|
|
return new _ActiveResult(this.source, explicitPos, this.result, from, to);
|
|
if (this.result.update && (updated = this.result.update(this.result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
|
|
return new _ActiveResult(this.source, explicitPos, updated, updated.from, (_a = updated.to) !== null && _a !== void 0 ? _a : cur(tr.state));
|
|
return new ActiveSource(this.source, 1, explicitPos);
|
|
}
|
|
handleChange(tr) {
|
|
return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(
|
|
this.source,
|
|
0
|
|
/* Inactive */
|
|
) : this.map(tr.changes);
|
|
}
|
|
map(mapping) {
|
|
return mapping.empty ? this : new _ActiveResult(this.source, this.explicitPos < 0 ? -1 : mapping.mapPos(this.explicitPos), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1));
|
|
}
|
|
};
|
|
function checkValid(validFor, state, from, to) {
|
|
if (!validFor)
|
|
return false;
|
|
let text = state.sliceDoc(from, to);
|
|
return typeof validFor == "function" ? validFor(text, from, to, state) : ensureAnchor(validFor, true).test(text);
|
|
}
|
|
var setActiveEffect = /* @__PURE__ */ import_state2.StateEffect.define({
|
|
map(sources, mapping) {
|
|
return sources.map((s) => s.map(mapping));
|
|
}
|
|
});
|
|
var setSelectedEffect = /* @__PURE__ */ import_state2.StateEffect.define();
|
|
var completionState = /* @__PURE__ */ import_state2.StateField.define({
|
|
create() {
|
|
return CompletionState.start();
|
|
},
|
|
update(value, tr) {
|
|
return value.update(tr);
|
|
},
|
|
provide: (f) => [
|
|
import_view2.showTooltip.from(f, (val) => val.tooltip),
|
|
import_view2.EditorView.contentAttributes.from(f, (state) => state.attrs)
|
|
]
|
|
});
|
|
function applyCompletion(view, option) {
|
|
const apply = option.completion.apply || option.completion.label;
|
|
let result = view.state.field(completionState).active.find((a) => a.source == option.source);
|
|
if (!(result instanceof ActiveResult))
|
|
return false;
|
|
if (typeof apply == "string")
|
|
view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, apply, result.from, result.to)), { annotations: pickedCompletion.of(option.completion) }));
|
|
else
|
|
apply(view, option.completion, result.from, result.to);
|
|
return true;
|
|
}
|
|
function moveCompletionSelection(forward, by = "option") {
|
|
return (view) => {
|
|
let cState = view.state.field(completionState, false);
|
|
if (!cState || !cState.open || cState.open.disabled || Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
return false;
|
|
let step = 1, tooltip;
|
|
if (by == "page" && (tooltip = (0, import_view2.getTooltip)(view, cState.open.tooltip)))
|
|
step = Math.max(2, Math.floor(tooltip.dom.offsetHeight / tooltip.dom.querySelector("li").offsetHeight) - 1);
|
|
let { length } = cState.open.options;
|
|
let selected = cState.open.selected > -1 ? cState.open.selected + step * (forward ? 1 : -1) : forward ? 0 : length - 1;
|
|
if (selected < 0)
|
|
selected = by == "page" ? 0 : length - 1;
|
|
else if (selected >= length)
|
|
selected = by == "page" ? length - 1 : 0;
|
|
view.dispatch({ effects: setSelectedEffect.of(selected) });
|
|
return true;
|
|
};
|
|
}
|
|
var acceptCompletion = (view) => {
|
|
let cState = view.state.field(completionState, false);
|
|
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 || cState.open.disabled || Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
return false;
|
|
return applyCompletion(view, cState.open.options[cState.open.selected]);
|
|
};
|
|
var startCompletion = (view) => {
|
|
let cState = view.state.field(completionState, false);
|
|
if (!cState)
|
|
return false;
|
|
view.dispatch({ effects: startCompletionEffect.of(true) });
|
|
return true;
|
|
};
|
|
var closeCompletion = (view) => {
|
|
let cState = view.state.field(completionState, false);
|
|
if (!cState || !cState.active.some(
|
|
(a) => a.state != 0
|
|
/* Inactive */
|
|
))
|
|
return false;
|
|
view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
return true;
|
|
};
|
|
var RunningQuery = class {
|
|
constructor(active, context) {
|
|
this.active = active;
|
|
this.context = context;
|
|
this.time = Date.now();
|
|
this.updates = [];
|
|
this.done = void 0;
|
|
}
|
|
};
|
|
var DebounceTime = 50;
|
|
var MaxUpdateCount = 50;
|
|
var MinAbortTime = 1e3;
|
|
var completionPlugin = /* @__PURE__ */ import_view2.ViewPlugin.fromClass(class {
|
|
constructor(view) {
|
|
this.view = view;
|
|
this.debounceUpdate = -1;
|
|
this.running = [];
|
|
this.debounceAccept = -1;
|
|
this.composing = 0;
|
|
for (let active of view.state.field(completionState).active)
|
|
if (active.state == 1)
|
|
this.startQuery(active);
|
|
}
|
|
update(update) {
|
|
let cState = update.state.field(completionState);
|
|
if (!update.selectionSet && !update.docChanged && update.startState.field(completionState) == cState)
|
|
return;
|
|
let doesReset = update.transactions.some((tr) => {
|
|
return (tr.selection || tr.docChanged) && !getUserEvent(tr);
|
|
});
|
|
for (let i = 0; i < this.running.length; i++) {
|
|
let query = this.running[i];
|
|
if (doesReset || query.updates.length + update.transactions.length > MaxUpdateCount && Date.now() - query.time > MinAbortTime) {
|
|
for (let handler of query.context.abortListeners) {
|
|
try {
|
|
handler();
|
|
} catch (e) {
|
|
(0, import_view2.logException)(this.view.state, e);
|
|
}
|
|
}
|
|
query.context.abortListeners = null;
|
|
this.running.splice(i--, 1);
|
|
} else {
|
|
query.updates.push(...update.transactions);
|
|
}
|
|
}
|
|
if (this.debounceUpdate > -1)
|
|
clearTimeout(this.debounceUpdate);
|
|
this.debounceUpdate = cState.active.some((a) => a.state == 1 && !this.running.some((q) => q.active.source == a.source)) ? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
|
|
if (this.composing != 0)
|
|
for (let tr of update.transactions) {
|
|
if (getUserEvent(tr) == "input")
|
|
this.composing = 2;
|
|
else if (this.composing == 2 && tr.selection)
|
|
this.composing = 3;
|
|
}
|
|
}
|
|
startUpdate() {
|
|
this.debounceUpdate = -1;
|
|
let { state } = this.view, cState = state.field(completionState);
|
|
for (let active of cState.active) {
|
|
if (active.state == 1 && !this.running.some((r) => r.active.source == active.source))
|
|
this.startQuery(active);
|
|
}
|
|
}
|
|
startQuery(active) {
|
|
let { state } = this.view, pos = cur(state);
|
|
let context = new CompletionContext(state, pos, active.explicitPos == pos);
|
|
let pending = new RunningQuery(active, context);
|
|
this.running.push(pending);
|
|
Promise.resolve(active.source(context)).then((result) => {
|
|
if (!pending.context.aborted) {
|
|
pending.done = result || null;
|
|
this.scheduleAccept();
|
|
}
|
|
}, (err) => {
|
|
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
(0, import_view2.logException)(this.view.state, err);
|
|
});
|
|
}
|
|
scheduleAccept() {
|
|
if (this.running.every((q) => q.done !== void 0))
|
|
this.accept();
|
|
else if (this.debounceAccept < 0)
|
|
this.debounceAccept = setTimeout(() => this.accept(), DebounceTime);
|
|
}
|
|
// For each finished query in this.running, try to create a result
|
|
// or, if appropriate, restart the query.
|
|
accept() {
|
|
var _a;
|
|
if (this.debounceAccept > -1)
|
|
clearTimeout(this.debounceAccept);
|
|
this.debounceAccept = -1;
|
|
let updated = [];
|
|
let conf = this.view.state.facet(completionConfig);
|
|
for (let i = 0; i < this.running.length; i++) {
|
|
let query = this.running[i];
|
|
if (query.done === void 0)
|
|
continue;
|
|
this.running.splice(i--, 1);
|
|
if (query.done) {
|
|
let active = new ActiveResult(query.active.source, query.active.explicitPos, query.done, query.done.from, (_a = query.done.to) !== null && _a !== void 0 ? _a : cur(query.updates.length ? query.updates[0].startState : this.view.state));
|
|
for (let tr of query.updates)
|
|
active = active.update(tr, conf);
|
|
if (active.hasResult()) {
|
|
updated.push(active);
|
|
continue;
|
|
}
|
|
}
|
|
let current = this.view.state.field(completionState).active.find((a) => a.source == query.active.source);
|
|
if (current && current.state == 1) {
|
|
if (query.done == null) {
|
|
let active = new ActiveSource(
|
|
query.active.source,
|
|
0
|
|
/* Inactive */
|
|
);
|
|
for (let tr of query.updates)
|
|
active = active.update(tr, conf);
|
|
if (active.state != 1)
|
|
updated.push(active);
|
|
} else {
|
|
this.startQuery(current);
|
|
}
|
|
}
|
|
}
|
|
if (updated.length)
|
|
this.view.dispatch({ effects: setActiveEffect.of(updated) });
|
|
}
|
|
}, {
|
|
eventHandlers: {
|
|
blur(event) {
|
|
let state = this.view.state.field(completionState, false);
|
|
if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur) {
|
|
let dialog = state.open && (0, import_view2.getTooltip)(this.view, state.open.tooltip);
|
|
if (!dialog || !dialog.dom.contains(event.relatedTarget))
|
|
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
}
|
|
},
|
|
compositionstart() {
|
|
this.composing = 1;
|
|
},
|
|
compositionend() {
|
|
if (this.composing == 3) {
|
|
setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
|
|
}
|
|
this.composing = 0;
|
|
}
|
|
}
|
|
});
|
|
var baseTheme = /* @__PURE__ */ import_view2.EditorView.baseTheme({
|
|
".cm-tooltip.cm-tooltip-autocomplete": {
|
|
"& > ul": {
|
|
fontFamily: "monospace",
|
|
whiteSpace: "nowrap",
|
|
overflow: "hidden auto",
|
|
maxWidth_fallback: "700px",
|
|
maxWidth: "min(700px, 95vw)",
|
|
minWidth: "250px",
|
|
maxHeight: "10em",
|
|
height: "100%",
|
|
listStyle: "none",
|
|
margin: 0,
|
|
padding: 0,
|
|
"& > li, & > completion-section": {
|
|
padding: "1px 3px",
|
|
lineHeight: 1.2
|
|
},
|
|
"& > li": {
|
|
overflowX: "hidden",
|
|
textOverflow: "ellipsis",
|
|
cursor: "pointer"
|
|
},
|
|
"& > completion-section": {
|
|
display: "list-item",
|
|
borderBottom: "1px solid silver",
|
|
paddingLeft: "0.5em",
|
|
opacity: 0.7
|
|
}
|
|
}
|
|
},
|
|
"&light .cm-tooltip-autocomplete ul li[aria-selected]": {
|
|
background: "#17c",
|
|
color: "white"
|
|
},
|
|
"&light .cm-tooltip-autocomplete-disabled ul li[aria-selected]": {
|
|
background: "#777"
|
|
},
|
|
"&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
|
|
background: "#347",
|
|
color: "white"
|
|
},
|
|
"&dark .cm-tooltip-autocomplete-disabled ul li[aria-selected]": {
|
|
background: "#444"
|
|
},
|
|
".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
|
|
content: '"\xB7\xB7\xB7"',
|
|
opacity: 0.5,
|
|
display: "block",
|
|
textAlign: "center"
|
|
},
|
|
".cm-tooltip.cm-completionInfo": {
|
|
position: "absolute",
|
|
padding: "3px 9px",
|
|
width: "max-content",
|
|
maxWidth: `${400}px`,
|
|
boxSizing: "border-box"
|
|
},
|
|
".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
|
|
".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
|
|
".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30}px` },
|
|
".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30}px` },
|
|
"&light .cm-snippetField": { backgroundColor: "#00000022" },
|
|
"&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
|
|
".cm-snippetFieldPosition": {
|
|
verticalAlign: "text-top",
|
|
width: 0,
|
|
height: "1.15em",
|
|
display: "inline-block",
|
|
margin: "0 -0.7px -.7em",
|
|
borderLeft: "1.4px dotted #888"
|
|
},
|
|
".cm-completionMatchedText": {
|
|
textDecoration: "underline"
|
|
},
|
|
".cm-completionDetail": {
|
|
marginLeft: "0.5em",
|
|
fontStyle: "italic"
|
|
},
|
|
".cm-completionIcon": {
|
|
fontSize: "90%",
|
|
width: ".8em",
|
|
display: "inline-block",
|
|
textAlign: "center",
|
|
paddingRight: ".6em",
|
|
opacity: "0.6",
|
|
boxSizing: "content-box"
|
|
},
|
|
".cm-completionIcon-function, .cm-completionIcon-method": {
|
|
"&:after": { content: "'\u0192'" }
|
|
},
|
|
".cm-completionIcon-class": {
|
|
"&:after": { content: "'\u25CB'" }
|
|
},
|
|
".cm-completionIcon-interface": {
|
|
"&:after": { content: "'\u25CC'" }
|
|
},
|
|
".cm-completionIcon-variable": {
|
|
"&:after": { content: "'\u{1D465}'" }
|
|
},
|
|
".cm-completionIcon-constant": {
|
|
"&:after": { content: "'\u{1D436}'" }
|
|
},
|
|
".cm-completionIcon-type": {
|
|
"&:after": { content: "'\u{1D461}'" }
|
|
},
|
|
".cm-completionIcon-enum": {
|
|
"&:after": { content: "'\u222A'" }
|
|
},
|
|
".cm-completionIcon-property": {
|
|
"&:after": { content: "'\u25A1'" }
|
|
},
|
|
".cm-completionIcon-keyword": {
|
|
"&:after": { content: "'\u{1F511}\uFE0E'" }
|
|
// Disable emoji rendering
|
|
},
|
|
".cm-completionIcon-namespace": {
|
|
"&:after": { content: "'\u25A2'" }
|
|
},
|
|
".cm-completionIcon-text": {
|
|
"&:after": { content: "'abc'", fontSize: "50%", verticalAlign: "middle" }
|
|
}
|
|
});
|
|
var defaults = {
|
|
brackets: ["(", "[", "{", "'", '"'],
|
|
before: ")]}:;>",
|
|
stringPrefixes: []
|
|
};
|
|
var closeBracketEffect = /* @__PURE__ */ import_state2.StateEffect.define({
|
|
map(value, mapping) {
|
|
let mapped = mapping.mapPos(value, -1, import_state2.MapMode.TrackAfter);
|
|
return mapped == null ? void 0 : mapped;
|
|
}
|
|
});
|
|
var closedBracket = /* @__PURE__ */ new class extends import_state2.RangeValue {
|
|
}();
|
|
closedBracket.startSide = 1;
|
|
closedBracket.endSide = -1;
|
|
var bracketState = /* @__PURE__ */ import_state2.StateField.define({
|
|
create() {
|
|
return import_state2.RangeSet.empty;
|
|
},
|
|
update(value, tr) {
|
|
if (tr.selection) {
|
|
let lineStart = tr.state.doc.lineAt(tr.selection.main.head).from;
|
|
let prevLineStart = tr.startState.doc.lineAt(tr.startState.selection.main.head).from;
|
|
if (lineStart != tr.changes.mapPos(prevLineStart, -1))
|
|
value = import_state2.RangeSet.empty;
|
|
}
|
|
value = value.map(tr.changes);
|
|
for (let effect of tr.effects)
|
|
if (effect.is(closeBracketEffect))
|
|
value = value.update({ add: [closedBracket.range(effect.value, effect.value + 1)] });
|
|
return value;
|
|
}
|
|
});
|
|
function closeBrackets() {
|
|
return [inputHandler, bracketState];
|
|
}
|
|
var definedClosing = "()[]{}<>";
|
|
function closing(ch) {
|
|
for (let i = 0; i < definedClosing.length; i += 2)
|
|
if (definedClosing.charCodeAt(i) == ch)
|
|
return definedClosing.charAt(i + 1);
|
|
return (0, import_state2.fromCodePoint)(ch < 128 ? ch : ch + 1);
|
|
}
|
|
function config(state, pos) {
|
|
return state.languageDataAt("closeBrackets", pos)[0] || defaults;
|
|
}
|
|
var android = typeof navigator == "object" && /* @__PURE__ */ /Android\b/.test(navigator.userAgent);
|
|
var inputHandler = /* @__PURE__ */ import_view2.EditorView.inputHandler.of((view, from, to, insert) => {
|
|
if ((android ? view.composing : view.compositionStarted) || view.state.readOnly)
|
|
return false;
|
|
let sel = view.state.selection.main;
|
|
if (insert.length > 2 || insert.length == 2 && (0, import_state2.codePointSize)((0, import_state2.codePointAt)(insert, 0)) == 1 || from != sel.from || to != sel.to)
|
|
return false;
|
|
let tr = insertBracket(view.state, insert);
|
|
if (!tr)
|
|
return false;
|
|
view.dispatch(tr);
|
|
return true;
|
|
});
|
|
var deleteBracketPair = ({ state, dispatch }) => {
|
|
if (state.readOnly)
|
|
return false;
|
|
let conf = config(state, state.selection.main.head);
|
|
let tokens = conf.brackets || defaults.brackets;
|
|
let dont = null, changes = state.changeByRange((range) => {
|
|
if (range.empty) {
|
|
let before = prevChar(state.doc, range.head);
|
|
for (let token of tokens) {
|
|
if (token == before && nextChar(state.doc, range.head) == closing((0, import_state2.codePointAt)(token, 0)))
|
|
return {
|
|
changes: { from: range.head - token.length, to: range.head + token.length },
|
|
range: import_state2.EditorSelection.cursor(range.head - token.length)
|
|
};
|
|
}
|
|
}
|
|
return { range: dont = range };
|
|
});
|
|
if (!dont)
|
|
dispatch(state.update(changes, { scrollIntoView: true, userEvent: "delete.backward" }));
|
|
return !dont;
|
|
};
|
|
var closeBracketsKeymap = [
|
|
{ key: "Backspace", run: deleteBracketPair }
|
|
];
|
|
function insertBracket(state, bracket) {
|
|
let conf = config(state, state.selection.main.head);
|
|
let tokens = conf.brackets || defaults.brackets;
|
|
for (let tok of tokens) {
|
|
let closed = closing((0, import_state2.codePointAt)(tok, 0));
|
|
if (bracket == tok)
|
|
return closed == tok ? handleSame(state, tok, tokens.indexOf(tok + tok + tok) > -1, conf) : handleOpen(state, tok, closed, conf.before || defaults.before);
|
|
if (bracket == closed && closedBracketAt(state, state.selection.main.from))
|
|
return handleClose(state, tok, closed);
|
|
}
|
|
return null;
|
|
}
|
|
function closedBracketAt(state, pos) {
|
|
let found = false;
|
|
state.field(bracketState).between(0, state.doc.length, (from) => {
|
|
if (from == pos)
|
|
found = true;
|
|
});
|
|
return found;
|
|
}
|
|
function nextChar(doc, pos) {
|
|
let next = doc.sliceString(pos, pos + 2);
|
|
return next.slice(0, (0, import_state2.codePointSize)((0, import_state2.codePointAt)(next, 0)));
|
|
}
|
|
function prevChar(doc, pos) {
|
|
let prev = doc.sliceString(pos - 2, pos);
|
|
return (0, import_state2.codePointSize)((0, import_state2.codePointAt)(prev, 0)) == prev.length ? prev : prev.slice(1);
|
|
}
|
|
function handleOpen(state, open, close, closeBefore) {
|
|
let dont = null, changes = state.changeByRange((range) => {
|
|
if (!range.empty)
|
|
return {
|
|
changes: [{ insert: open, from: range.from }, { insert: close, from: range.to }],
|
|
effects: closeBracketEffect.of(range.to + open.length),
|
|
range: import_state2.EditorSelection.range(range.anchor + open.length, range.head + open.length)
|
|
};
|
|
let next = nextChar(state.doc, range.head);
|
|
if (!next || /\s/.test(next) || closeBefore.indexOf(next) > -1)
|
|
return {
|
|
changes: { insert: open + close, from: range.head },
|
|
effects: closeBracketEffect.of(range.head + open.length),
|
|
range: import_state2.EditorSelection.cursor(range.head + open.length)
|
|
};
|
|
return { range: dont = range };
|
|
});
|
|
return dont ? null : state.update(changes, {
|
|
scrollIntoView: true,
|
|
userEvent: "input.type"
|
|
});
|
|
}
|
|
function handleClose(state, _open, close) {
|
|
let dont = null, changes = state.changeByRange((range) => {
|
|
if (range.empty && nextChar(state.doc, range.head) == close)
|
|
return {
|
|
changes: { from: range.head, to: range.head + close.length, insert: close },
|
|
range: import_state2.EditorSelection.cursor(range.head + close.length)
|
|
};
|
|
return dont = { range };
|
|
});
|
|
return dont ? null : state.update(changes, {
|
|
scrollIntoView: true,
|
|
userEvent: "input.type"
|
|
});
|
|
}
|
|
function handleSame(state, token, allowTriple, config3) {
|
|
let stringPrefixes = config3.stringPrefixes || defaults.stringPrefixes;
|
|
let dont = null, changes = state.changeByRange((range) => {
|
|
if (!range.empty)
|
|
return {
|
|
changes: [{ insert: token, from: range.from }, { insert: token, from: range.to }],
|
|
effects: closeBracketEffect.of(range.to + token.length),
|
|
range: import_state2.EditorSelection.range(range.anchor + token.length, range.head + token.length)
|
|
};
|
|
let pos = range.head, next = nextChar(state.doc, pos), start;
|
|
if (next == token) {
|
|
if (nodeStart(state, pos)) {
|
|
return {
|
|
changes: { insert: token + token, from: pos },
|
|
effects: closeBracketEffect.of(pos + token.length),
|
|
range: import_state2.EditorSelection.cursor(pos + token.length)
|
|
};
|
|
} else if (closedBracketAt(state, pos)) {
|
|
let isTriple = allowTriple && state.sliceDoc(pos, pos + token.length * 3) == token + token + token;
|
|
let content = isTriple ? token + token + token : token;
|
|
return {
|
|
changes: { from: pos, to: pos + content.length, insert: content },
|
|
range: import_state2.EditorSelection.cursor(pos + content.length)
|
|
};
|
|
}
|
|
} else if (allowTriple && state.sliceDoc(pos - 2 * token.length, pos) == token + token && (start = canStartStringAt(state, pos - 2 * token.length, stringPrefixes)) > -1 && nodeStart(state, start)) {
|
|
return {
|
|
changes: { insert: token + token + token + token, from: pos },
|
|
effects: closeBracketEffect.of(pos + token.length),
|
|
range: import_state2.EditorSelection.cursor(pos + token.length)
|
|
};
|
|
} else if (state.charCategorizer(pos)(next) != import_state2.CharCategory.Word) {
|
|
if (canStartStringAt(state, pos, stringPrefixes) > -1 && !probablyInString(state, pos, token, stringPrefixes))
|
|
return {
|
|
changes: { insert: token + token, from: pos },
|
|
effects: closeBracketEffect.of(pos + token.length),
|
|
range: import_state2.EditorSelection.cursor(pos + token.length)
|
|
};
|
|
}
|
|
return { range: dont = range };
|
|
});
|
|
return dont ? null : state.update(changes, {
|
|
scrollIntoView: true,
|
|
userEvent: "input.type"
|
|
});
|
|
}
|
|
function nodeStart(state, pos) {
|
|
let tree = (0, import_language2.syntaxTree)(state).resolveInner(pos + 1);
|
|
return tree.parent && tree.from == pos;
|
|
}
|
|
function probablyInString(state, pos, quoteToken, prefixes) {
|
|
let node = (0, import_language2.syntaxTree)(state).resolveInner(pos, -1);
|
|
let maxPrefix = prefixes.reduce((m, p) => Math.max(m, p.length), 0);
|
|
for (let i = 0; i < 5; i++) {
|
|
let start = state.sliceDoc(node.from, Math.min(node.to, node.from + quoteToken.length + maxPrefix));
|
|
let quotePos = start.indexOf(quoteToken);
|
|
if (!quotePos || quotePos > -1 && prefixes.indexOf(start.slice(0, quotePos)) > -1) {
|
|
let first = node.firstChild;
|
|
while (first && first.from == node.from && first.to - first.from > quoteToken.length + quotePos) {
|
|
if (state.sliceDoc(first.to - quoteToken.length, first.to) == quoteToken)
|
|
return false;
|
|
first = first.firstChild;
|
|
}
|
|
return true;
|
|
}
|
|
let parent = node.to == pos && node.parent;
|
|
if (!parent)
|
|
break;
|
|
node = parent;
|
|
}
|
|
return false;
|
|
}
|
|
function canStartStringAt(state, pos, prefixes) {
|
|
let charCat = state.charCategorizer(pos);
|
|
if (charCat(state.sliceDoc(pos - 1, pos)) != import_state2.CharCategory.Word)
|
|
return pos;
|
|
for (let prefix of prefixes) {
|
|
let start = pos - prefix.length;
|
|
if (state.sliceDoc(start, pos) == prefix && charCat(state.sliceDoc(start - 1, start)) != import_state2.CharCategory.Word)
|
|
return start;
|
|
}
|
|
return -1;
|
|
}
|
|
function autocompletion(config3 = {}) {
|
|
return [
|
|
completionState,
|
|
completionConfig.of(config3),
|
|
completionPlugin,
|
|
completionKeymapExt,
|
|
baseTheme
|
|
];
|
|
}
|
|
var completionKeymap = [
|
|
{ key: "Ctrl-Space", run: startCompletion },
|
|
{ key: "Escape", run: closeCompletion },
|
|
{ key: "ArrowDown", run: /* @__PURE__ */ moveCompletionSelection(true) },
|
|
{ key: "ArrowUp", run: /* @__PURE__ */ moveCompletionSelection(false) },
|
|
{ key: "PageDown", run: /* @__PURE__ */ moveCompletionSelection(true, "page") },
|
|
{ key: "PageUp", run: /* @__PURE__ */ moveCompletionSelection(false, "page") },
|
|
{ key: "Enter", run: acceptCompletion }
|
|
];
|
|
var completionKeymapExt = /* @__PURE__ */ import_state2.Prec.highest(/* @__PURE__ */ import_view2.keymap.computeN([completionConfig], (state) => state.facet(completionConfig).defaultKeymap ? [completionKeymap] : []));
|
|
|
|
// src/codemirror-extensions/basic-extensions.ts
|
|
var import_search2 = require("@codemirror/search");
|
|
var import_lint = require("@codemirror/lint");
|
|
|
|
// src/codemirror-extensions/obsidian-theme.ts
|
|
var import_view3 = require("@codemirror/view");
|
|
var import_language3 = require("@codemirror/language");
|
|
var import_highlight = require("@lezer/highlight");
|
|
var config2 = {
|
|
name: "obsidian",
|
|
dark: false,
|
|
background: "var(--background-primary)",
|
|
foreground: "var(--text-normal)",
|
|
selection: "var(--text-selection)",
|
|
cursor: "var(--text-normal)",
|
|
dropdownBackground: "var(--background-primary)",
|
|
dropdownBorder: "var(--background-modifier-border)",
|
|
activeLine: "var(--background-secondary)",
|
|
activeLineNumber: "var(--text-normal)",
|
|
matchingBracket: "var(--background-modifier-accent)",
|
|
keyword: "#d73a49",
|
|
storage: "#d73a49",
|
|
variable: "var(--text-normal)",
|
|
parameter: "var(--text-accent-hover)",
|
|
function: "var(--text-accent-hover)",
|
|
string: "var(--text-accent)",
|
|
constant: "var(--text-accent-hover)",
|
|
type: "var(--text-accent-hover)",
|
|
class: "#6f42c1",
|
|
number: "var(--text-accent-hover)",
|
|
comment: "var(--text-faint)",
|
|
invalid: "var(--text-error)",
|
|
regexp: "#032f62",
|
|
monospace: "var(--font-monospace)"
|
|
};
|
|
var obsidianTheme = import_view3.EditorView.theme(
|
|
{
|
|
"&": {
|
|
color: config2.foreground,
|
|
backgroundColor: config2.background
|
|
},
|
|
".cm-scroller": { fontFamily: config2.monospace },
|
|
".cm-content": { caretColor: config2.cursor },
|
|
"&.cm-focused .cm-cursor": { borderLeftColor: config2.cursor },
|
|
".cm-selectionBackground, &.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, & ::selection": { backgroundColor: config2.selection },
|
|
".cm-panels": {
|
|
backgroundColor: config2.dropdownBackground,
|
|
color: config2.foreground
|
|
},
|
|
".cm-panels.cm-panels-top": { borderBottom: "2px solid black" },
|
|
".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" },
|
|
".cm-searchMatch": {
|
|
backgroundColor: config2.dropdownBackground,
|
|
outline: `1px solid ${config2.dropdownBorder}`
|
|
},
|
|
".cm-searchMatch.cm-searchMatch-selected": {
|
|
backgroundColor: config2.selection
|
|
},
|
|
".cm-activeLine": { backgroundColor: config2.activeLine },
|
|
".cm-activeLineGutter": {
|
|
backgroundColor: config2.activeLine,
|
|
".cm-lineNumbers &": {
|
|
color: config2.activeLineNumber
|
|
}
|
|
},
|
|
".cm-selectionMatch": { backgroundColor: config2.selection },
|
|
".cm-matchingBracket, .cm-nonmatchingBracket": {
|
|
backgroundColor: config2.matchingBracket,
|
|
outline: "none"
|
|
},
|
|
".cm-gutters": {
|
|
backgroundColor: config2.background,
|
|
color: config2.comment,
|
|
borderRight: "1px solid var(--background-modifier-border)"
|
|
},
|
|
".cm-lineNumbers, .cm-gutterElement": { color: "inherit" },
|
|
".cm-foldPlaceholder": {
|
|
backgroundColor: "transparent",
|
|
border: "none",
|
|
color: config2.foreground
|
|
},
|
|
".cm-tooltip": {
|
|
border: `1px solid ${config2.dropdownBorder}`,
|
|
backgroundColor: config2.dropdownBackground,
|
|
color: config2.foreground
|
|
},
|
|
".cm-tooltip.cm-tooltip-autocomplete": {
|
|
"& > ul": {
|
|
fontFamily: config2.monospace,
|
|
"& > li[aria-selected]": {
|
|
background: config2.selection,
|
|
color: config2.foreground
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{ dark: config2.dark }
|
|
);
|
|
var obsidianHighlightStyle = import_language3.HighlightStyle.define([
|
|
{ tag: import_highlight.tags.keyword, color: config2.keyword },
|
|
{
|
|
tag: [import_highlight.tags.name, import_highlight.tags.deleted, import_highlight.tags.character, import_highlight.tags.macroName],
|
|
color: config2.variable
|
|
},
|
|
{ tag: [import_highlight.tags.propertyName], color: config2.function },
|
|
{
|
|
tag: [
|
|
import_highlight.tags.processingInstruction,
|
|
import_highlight.tags.string,
|
|
import_highlight.tags.inserted,
|
|
import_highlight.tags.special(import_highlight.tags.string)
|
|
],
|
|
color: config2.string
|
|
},
|
|
{ tag: [import_highlight.tags.function(import_highlight.tags.variableName), import_highlight.tags.labelName], color: config2.function },
|
|
{
|
|
tag: [import_highlight.tags.color, import_highlight.tags.constant(import_highlight.tags.name), import_highlight.tags.standard(import_highlight.tags.name)],
|
|
color: config2.constant
|
|
},
|
|
{ tag: [import_highlight.tags.definition(import_highlight.tags.name), import_highlight.tags.separator], color: config2.variable },
|
|
{ tag: [import_highlight.tags.className], color: config2.class },
|
|
{
|
|
tag: [
|
|
import_highlight.tags.number,
|
|
import_highlight.tags.changed,
|
|
import_highlight.tags.annotation,
|
|
import_highlight.tags.modifier,
|
|
import_highlight.tags.self,
|
|
import_highlight.tags.namespace
|
|
],
|
|
color: config2.number
|
|
},
|
|
{ tag: [import_highlight.tags.typeName], color: config2.type, fontStyle: config2.type },
|
|
{ tag: [import_highlight.tags.operator, import_highlight.tags.operatorKeyword], color: config2.keyword },
|
|
{ tag: [import_highlight.tags.url, import_highlight.tags.escape, import_highlight.tags.regexp, import_highlight.tags.link], color: config2.regexp },
|
|
{ tag: [import_highlight.tags.meta, import_highlight.tags.comment], color: config2.comment },
|
|
{
|
|
tag: [import_highlight.tags.atom, import_highlight.tags.bool, import_highlight.tags.special(import_highlight.tags.variableName)],
|
|
color: config2.variable
|
|
},
|
|
{ tag: import_highlight.tags.invalid, color: config2.invalid }
|
|
]);
|
|
var obsidian = [
|
|
obsidianTheme,
|
|
(0, import_language3.syntaxHighlighting)(obsidianHighlightStyle)
|
|
];
|
|
|
|
// node_modules/@lezer/css/dist/index.es.js
|
|
var import_lr = require("@lezer/lr");
|
|
var import_highlight2 = require("@lezer/highlight");
|
|
var descendantOp = 94;
|
|
var Unit = 1;
|
|
var callee = 95;
|
|
var identifier = 96;
|
|
var VariableName = 2;
|
|
var space = [
|
|
9,
|
|
10,
|
|
11,
|
|
12,
|
|
13,
|
|
32,
|
|
133,
|
|
160,
|
|
5760,
|
|
8192,
|
|
8193,
|
|
8194,
|
|
8195,
|
|
8196,
|
|
8197,
|
|
8198,
|
|
8199,
|
|
8200,
|
|
8201,
|
|
8202,
|
|
8232,
|
|
8233,
|
|
8239,
|
|
8287,
|
|
12288
|
|
];
|
|
var colon = 58;
|
|
var parenL = 40;
|
|
var underscore = 95;
|
|
var bracketL = 91;
|
|
var dash = 45;
|
|
var period = 46;
|
|
var hash = 35;
|
|
var percent = 37;
|
|
function isAlpha(ch) {
|
|
return ch >= 65 && ch <= 90 || ch >= 97 && ch <= 122 || ch >= 161;
|
|
}
|
|
function isDigit(ch) {
|
|
return ch >= 48 && ch <= 57;
|
|
}
|
|
var identifiers = new import_lr.ExternalTokenizer((input, stack) => {
|
|
for (let inside = false, dashes = 0, i = 0; ; i++) {
|
|
let { next } = input;
|
|
if (isAlpha(next) || next == dash || next == underscore || inside && isDigit(next)) {
|
|
if (!inside && (next != dash || i > 0)) inside = true;
|
|
if (dashes === i && next == dash) dashes++;
|
|
input.advance();
|
|
} else {
|
|
if (inside)
|
|
input.acceptToken(next == parenL ? callee : dashes == 2 && stack.canShift(VariableName) ? VariableName : identifier);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
var descendant = new import_lr.ExternalTokenizer((input) => {
|
|
if (space.includes(input.peek(-1))) {
|
|
let { next } = input;
|
|
if (isAlpha(next) || next == underscore || next == hash || next == period || next == bracketL || next == colon || next == dash)
|
|
input.acceptToken(descendantOp);
|
|
}
|
|
});
|
|
var unitToken = new import_lr.ExternalTokenizer((input) => {
|
|
if (!space.includes(input.peek(-1))) {
|
|
let { next } = input;
|
|
if (next == percent) {
|
|
input.advance();
|
|
input.acceptToken(Unit);
|
|
}
|
|
if (isAlpha(next)) {
|
|
do {
|
|
input.advance();
|
|
} while (isAlpha(input.next));
|
|
input.acceptToken(Unit);
|
|
}
|
|
}
|
|
});
|
|
var cssHighlighting = (0, import_highlight2.styleTags)({
|
|
"AtKeyword import charset namespace keyframes media supports": import_highlight2.tags.definitionKeyword,
|
|
"from to selector": import_highlight2.tags.keyword,
|
|
NamespaceName: import_highlight2.tags.namespace,
|
|
KeyframeName: import_highlight2.tags.labelName,
|
|
TagName: import_highlight2.tags.tagName,
|
|
ClassName: import_highlight2.tags.className,
|
|
PseudoClassName: import_highlight2.tags.constant(import_highlight2.tags.className),
|
|
IdName: import_highlight2.tags.labelName,
|
|
"FeatureName PropertyName": import_highlight2.tags.propertyName,
|
|
AttributeName: import_highlight2.tags.attributeName,
|
|
NumberLiteral: import_highlight2.tags.number,
|
|
KeywordQuery: import_highlight2.tags.keyword,
|
|
UnaryQueryOp: import_highlight2.tags.operatorKeyword,
|
|
"CallTag ValueName": import_highlight2.tags.atom,
|
|
VariableName: import_highlight2.tags.variableName,
|
|
Callee: import_highlight2.tags.operatorKeyword,
|
|
Unit: import_highlight2.tags.unit,
|
|
"UniversalSelector NestingSelector": import_highlight2.tags.definitionOperator,
|
|
MatchOp: import_highlight2.tags.compareOperator,
|
|
"ChildOp SiblingOp, LogicOp": import_highlight2.tags.logicOperator,
|
|
BinOp: import_highlight2.tags.arithmeticOperator,
|
|
Important: import_highlight2.tags.modifier,
|
|
Comment: import_highlight2.tags.blockComment,
|
|
ParenthesizedContent: import_highlight2.tags.special(import_highlight2.tags.name),
|
|
ColorLiteral: import_highlight2.tags.color,
|
|
StringLiteral: import_highlight2.tags.string,
|
|
":": import_highlight2.tags.punctuation,
|
|
"PseudoOp #": import_highlight2.tags.derefOperator,
|
|
"; ,": import_highlight2.tags.separator,
|
|
"( )": import_highlight2.tags.paren,
|
|
"[ ]": import_highlight2.tags.squareBracket,
|
|
"{ }": import_highlight2.tags.brace
|
|
});
|
|
var spec_callee = { __proto__: null, lang: 32, "nth-child": 32, "nth-last-child": 32, "nth-of-type": 32, "nth-last-of-type": 32, dir: 32, "host-context": 32, url: 60, "url-prefix": 60, domain: 60, regexp: 60, selector: 134 };
|
|
var spec_AtKeyword = { __proto__: null, "@import": 114, "@media": 138, "@charset": 142, "@namespace": 146, "@keyframes": 152, "@supports": 164 };
|
|
var spec_identifier = { __proto__: null, not: 128, only: 128, from: 158, to: 160 };
|
|
var parser = import_lr.LRParser.deserialize({
|
|
version: 14,
|
|
states: "7WQYQ[OOO#_Q[OOOOQP'#Cd'#CdOOQP'#Cc'#CcO#fQ[O'#CfO$YQXO'#CaO$aQ[O'#ChO$lQ[O'#DPO$qQ[O'#DTOOQP'#Ed'#EdO$vQdO'#DeO%bQ[O'#DrO$vQdO'#DtO%sQ[O'#DvO&OQ[O'#DyO&TQ[O'#EPO&cQ[O'#EROOQS'#Ec'#EcOOQS'#ET'#ETQYQ[OOO&jQXO'#CdO'_QWO'#DaO'dQWO'#EjO'oQ[O'#EjQOQWOOOOQP'#Cg'#CgOOQP,59Q,59QO#fQ[O,59QO'yQ[O'#EWO(eQWO,58{O(mQ[O,59SO$lQ[O,59kO$qQ[O,59oO'yQ[O,59sO'yQ[O,59uO'yQ[O,59vO(xQ[O'#D`OOQS,58{,58{OOQP'#Ck'#CkOOQO'#C}'#C}OOQP,59S,59SO)PQWO,59SO)UQWO,59SOOQP'#DR'#DROOQP,59k,59kOOQO'#DV'#DVO)ZQ`O,59oOOQS'#Cp'#CpO$vQdO'#CqO)cQvO'#CsO*pQtO,5:POOQO'#Cx'#CxO)UQWO'#CwO+UQWO'#CyOOQS'#Eg'#EgOOQO'#Dh'#DhO+ZQ[O'#DoO+iQWO'#EkO&TQ[O'#DmO+wQWO'#DpOOQO'#El'#ElO(hQWO,5:^O+|QpO,5:`OOQS'#Dx'#DxO,UQWO,5:bO,ZQ[O,5:bOOQO'#D{'#D{O,cQWO,5:eO,hQWO,5:kO,pQWO,5:mOOQS-E8R-E8RO$vQdO,59{O,xQ[O'#EYO-VQWO,5;UO-VQWO,5;UOOQP1G.l1G.lO-|QXO,5:rOOQO-E8U-E8UOOQS1G.g1G.gOOQP1G.n1G.nO)PQWO1G.nO)UQWO1G.nOOQP1G/V1G/VO.ZQ`O1G/ZO.tQXO1G/_O/[QXO1G/aO/rQXO1G/bO0YQWO,59zO0_Q[O'#DOO0fQdO'#CoOOQP1G/Z1G/ZO$vQdO1G/ZO0mQpO,59]OOQS,59_,59_O$vQdO,59aO0uQWO1G/kOOQS,59c,59cO0zQ!bO,59eO1SQWO'#DhO1_QWO,5:TO1dQWO,5:ZO&TQ[O,5:VO&TQ[O'#EZO1lQWO,5;VO1wQWO,5:XO'yQ[O,5:[OOQS1G/x1G/xOOQS1G/z1G/zOOQS1G/|1G/|O2YQWO1G/|O2_QdO'#D|OOQS1G0P1G0POOQS1G0V1G0VOOQS1G0X1G0XO2mQtO1G/gOOQO,5:t,5:tO3TQ[O,5:tOOQO-E8W-E8WO3bQWO1G0pOOQP7+$Y7+$YOOQP7+$u7+$uO$vQdO7+$uOOQS1G/f1G/fO3mQXO'#EiO3tQWO,59jO3yQtO'#EUO4nQdO'#EfO4xQWO,59ZO4}QpO7+$uOOQS1G.w1G.wOOQS1G.{1G.{OOQS7+%V7+%VO5VQWO1G/PO$vQdO1G/oOOQO1G/u1G/uOOQO1G/q1G/qO5[QWO,5:uOOQO-E8X-E8XO5jQXO1G/vOOQS7+%h7+%hO5qQYO'#CsO(hQWO'#E[O5yQdO,5:hOOQS,5:h,5:hO6XQtO'#EXO$vQdO'#EXO7VQdO7+%ROOQO7+%R7+%ROOQO1G0`1G0`O7jQpO<<HaO7rQWO,5;TOOQP1G/U1G/UOOQS-E8S-E8SO$vQdO'#EVO7zQWO,5;QOOQT1G.u1G.uOOQP<<Ha<<HaOOQS7+$k7+$kO8SQdO7+%ZOOQO7+%b7+%bOOQS,5:v,5:vOOQS-E8Y-E8YOOQS1G0S1G0SO8ZQtO,5:sOOQS-E8V-E8VOOQO<<Hm<<HmOOQPAN={AN={O9XQdO,5:qOOQO-E8T-E8TOOQO<<Hu<<Hu",
|
|
stateData: "9i~O#UOSROS~OUXOXXO]UO^UOtVOxWO!Y`O!ZYO!gZO!i[O!k]O!n^O!t_O#SQO#XSO~OQeOUXOXXO]UO^UOtVOxWO!Y`O!ZYO!gZO!i[O!k]O!n^O!t_O#SdO#XSO~O#P#^P~P!ZO#SiO~O]nO^nOplOtoOxpO|qO!PsO#QrO#XkO~O!RtO~P#kO`zO#RwO#SvO~O#S{O~O#S}O~OQ!WOb!QOf!WOh!WOn!VO#R!TO#S!PO#[!RO~Ob!YO!b![O!e!]O#S!XO!R#_P~Oh!bOn!VO#S!aO~O#S!dO~Ob!YO!b![O!e!]O#S!XO~O!W#_P~P%bO]WX]!UX^WXpWXtWXxWX|WX!PWX!RWX#QWX#XWX~O]!iO~O!W!jO#P#^X!Q#^X~O#P#^X!Q#^X~P!ZOUXOXXO]UO^UOtVOxWO#SQO#XSO~OplO!RtO~O`!sO#RwO#SvO~O!Q#^P~P!ZOb!zO~Ob!{O~Ov!|Oz!}O~OP#PObgXjgX!WgX!bgX!egX#SgXagXQgXfgXhgXngXpgX!VgX#PgX#RgX#[gXvgX!QgX~Ob!YOj#QO!b![O!e!]O#S!XO!W#_P~Ob#TO~Ob!YO!b![O!e!]O#S#UO~Op#YO!`#XO!R#_X!W#_X~Ob#]O~Oj#QO!W#_O~O!W#`O~Oh#aOn!VO~O!R#bO~O!RtO!`#XO~O!RtO!W#eO~O!W!|X#P!|X!Q!|X~P!ZO!W!jO#P#^a!Q#^a~O]nO^nOtoOxpO|qO!PsO#QrO#XkO~Op!za!R!zaa!za~P-bOv#lOz#mO~O]nO^nOtoOxpO#XkO~Op{i|{i!P{i!R{i#Q{ia{i~P.cOp}i|}i!P}i!R}i#Q}ia}i~P.cOp!Oi|!Oi!P!Oi!R!Oi#Q!Oia!Oi~P.cO!Q#nO~Oa#]P~P'yOa#YP~P$vOa#uOj#QO~O!W#wO~Oh#xOo#xO~O]!^Xa![X!`![X~O]#yO~Oa#zO!`#XO~Op#YO!R#_a!W#_a~O!`#XOp!aa!R!aa!W!aaa!aa~O!W$PO~O!Q$TO!q$RO!r$RO#[$QO~Oj#QOp$VO!V$XO!W!Ti#P!Ti!Q!Ti~P$vO!W!|a#P!|a!Q!|a~P!ZO!W!jO#P#^i!Q#^i~Oa#]X~P#kOa$]O~Oj#QOQ!xXa!xXb!xXf!xXh!xXn!xXp!xX#R!xX#S!xX#[!xX~Op$_Oa#YX~P$vOa$aO~Oj#QOv$bO~Oa$cO~O!`#XOp!}a!R!}a!W!}a~Oa$eO~P-bOP#PO!RgX~O!Q$hO!q$RO!r$RO#[$QO~Oj#QOQ!{Xb!{Xf!{Xh!{Xn!{Xp!{X!V!{X!W!{X#P!{X#R!{X#S!{X#[!{X!Q!{X~Op$VO!V$kO!W!Tq#P!Tq!Q!Tq~P$vOj#QOv$lO~OplOa#]a~Op$_Oa#Ya~Oa$oO~P$vOj#QOQ!{ab!{af!{ah!{an!{ap!{a!V!{a!W!{a#P!{a#R!{a#S!{a#[!{a!Q!{a~Oa!yap!ya~P$vOo#[j!Pj~",
|
|
goto: ",`#aPPPPP#bP#k#zP#k$Z#kPP$aPPP$g$p$pP%SP$pP$p%j%|PPP&f&l#kP&rP#kP&xP#kP#k#kPPP'O'b'oPP#bPP'v'v(Q'vP'vP'v'vP#bP#bP#bP(T#bP(W(ZPP#bP#bP(^(m({)R)])c)m)sPPPPPP)y*SP*o*rP+h+k+q+z_aOPcgt!j#hkXOPcglqrst!j!z#]#hkROPcglqrst!j!z#]#hQjSR!mkQxUR!qnQ!qzQ#S!UR#k!sq!WY[!Q!i!{!}#Q#f#m#r#y$V$W$_$d$mp!WY[!Q!i!{!}#Q#f#m#r#y$V$W$_$d$mT$R#b$Sq!UY[!Q!i!{!}#Q#f#m#r#y$V$W$_$d$mp!WY[!Q!i!{!}#Q#f#m#r#y$V$W$_$d$mQ!b]R#a!cQyUR!rnQ!qyR#k!rQ|VR!toQ!OWR!upQuTQ!pmQ#^!_Q#d!fQ#e!gR$f$RSfPtQ!lgQ#g!jR$Y#hZePgt!j#ha!^Z_`!S!Y![#X#YR#V!YR!c]R!e^R#c!eQcOSgPtU!hcg#hR#h!jQ#r!{U$^#r$d$mQ$d#yR$m$_Q$`#rR$n$`QmTS!om$[R$[#oQ$W#fR$j$WQ!kfS#i!k#jR#j!lQ#Z!ZR#}#ZQ$S#bR$g$S_bOPcgt!j#h^TOPcgt!j#hQ!nlQ!vqQ!wrQ!xsQ#o!zR$O#]R#s!{Q!SYQ!`[Q#O!QQ#f!i[#q!{#r#y$_$d$mQ#t!}Q#v#QS$U#f$WQ$Z#mR$i$VR#p!zQhPR!ytQ!_ZQ!g`R#R!SU!ZZ`!SQ!f_Q#W!YQ#[![Q#{#XR#|#Y",
|
|
nodeNames: "\u26A0 Unit VariableName Comment StyleSheet RuleSet UniversalSelector TagSelector TagName NestingSelector ClassSelector ClassName PseudoClassSelector : :: PseudoClassName PseudoClassName ) ( ArgList ValueName ParenthesizedValue ColorLiteral NumberLiteral StringLiteral BinaryExpression BinOp CallExpression Callee CallLiteral CallTag ParenthesizedContent , PseudoClassName ArgList IdSelector # IdName ] AttributeSelector [ AttributeName MatchOp ChildSelector ChildOp DescendantSelector SiblingSelector SiblingOp } { Block Declaration PropertyName Important ; ImportStatement AtKeyword import KeywordQuery FeatureQuery FeatureName BinaryQuery LogicOp UnaryQuery UnaryQueryOp ParenthesizedQuery SelectorQuery selector MediaStatement media CharsetStatement charset NamespaceStatement namespace NamespaceName KeyframesStatement keyframes KeyframeName KeyframeList from to SupportsStatement supports AtRule Styles",
|
|
maxTerm: 108,
|
|
nodeProps: [
|
|
["openedBy", 17, "(", 48, "{"],
|
|
["closedBy", 18, ")", 49, "}"]
|
|
],
|
|
propSources: [cssHighlighting],
|
|
skippedNodes: [0, 3],
|
|
repeatNodeCount: 8,
|
|
tokenData: "Lq~R!^OX$}X^%u^p$}pq%uqr)Xrs.Rst/utu6duv$}vw7^wx7oxy9^yz9oz{9t{|:_|}?Q}!O?c!O!P@Q!P!Q@i!Q![Cu![!]Dp!]!^El!^!_$}!_!`E}!`!aF`!a!b$}!b!cG[!c!}$}!}#OHt#O#P$}#P#QIV#Q#R6d#R#T$}#T#UIh#U#c$}#c#dJy#d#o$}#o#pK`#p#q6d#q#rKq#r#sLS#s#y$}#y#z%u#z$f$}$f$g%u$g#BY$}#BY#BZ%u#BZ$IS$}$IS$I_%u$I_$I|$}$I|$JO%u$JO$JT$}$JT$JU%u$JU$KV$}$KV$KW%u$KW&FU$}&FU&FV%u&FV;'S$};'S;=`Lk<%lO$}W%QSOy%^z;'S%^;'S;=`%o<%lO%^W%cSoWOy%^z;'S%^;'S;=`%o<%lO%^W%rP;=`<%l%^~%zh#U~OX%^X^'f^p%^pq'fqy%^z#y%^#y#z'f#z$f%^$f$g'f$g#BY%^#BY#BZ'f#BZ$IS%^$IS$I_'f$I_$I|%^$I|$JO'f$JO$JT%^$JT$JU'f$JU$KV%^$KV$KW'f$KW&FU%^&FU&FV'f&FV;'S%^;'S;=`%o<%lO%^~'mh#U~oWOX%^X^'f^p%^pq'fqy%^z#y%^#y#z'f#z$f%^$f$g'f$g#BY%^#BY#BZ'f#BZ$IS%^$IS$I_'f$I_$I|%^$I|$JO'f$JO$JT%^$JT$JU'f$JU$KV%^$KV$KW'f$KW&FU%^&FU&FV'f&FV;'S%^;'S;=`%o<%lO%^^)[UOy%^z#]%^#]#^)n#^;'S%^;'S;=`%o<%lO%^^)sUoWOy%^z#a%^#a#b*V#b;'S%^;'S;=`%o<%lO%^^*[UoWOy%^z#d%^#d#e*n#e;'S%^;'S;=`%o<%lO%^^*sUoWOy%^z#c%^#c#d+V#d;'S%^;'S;=`%o<%lO%^^+[UoWOy%^z#f%^#f#g+n#g;'S%^;'S;=`%o<%lO%^^+sUoWOy%^z#h%^#h#i,V#i;'S%^;'S;=`%o<%lO%^^,[UoWOy%^z#T%^#T#U,n#U;'S%^;'S;=`%o<%lO%^^,sUoWOy%^z#b%^#b#c-V#c;'S%^;'S;=`%o<%lO%^^-[UoWOy%^z#h%^#h#i-n#i;'S%^;'S;=`%o<%lO%^^-uS!VUoWOy%^z;'S%^;'S;=`%o<%lO%^~.UWOY.RZr.Rrs.ns#O.R#O#P.s#P;'S.R;'S;=`/o<%lO.R~.sOh~~.vRO;'S.R;'S;=`/P;=`O.R~/SXOY.RZr.Rrs.ns#O.R#O#P.s#P;'S.R;'S;=`/o;=`<%l.R<%lO.R~/rP;=`<%l.R_/zYtPOy%^z!Q%^!Q![0j![!c%^!c!i0j!i#T%^#T#Z0j#Z;'S%^;'S;=`%o<%lO%^^0oYoWOy%^z!Q%^!Q![1_![!c%^!c!i1_!i#T%^#T#Z1_#Z;'S%^;'S;=`%o<%lO%^^1dYoWOy%^z!Q%^!Q![2S![!c%^!c!i2S!i#T%^#T#Z2S#Z;'S%^;'S;=`%o<%lO%^^2ZYfUoWOy%^z!Q%^!Q![2y![!c%^!c!i2y!i#T%^#T#Z2y#Z;'S%^;'S;=`%o<%lO%^^3QYfUoWOy%^z!Q%^!Q![3p![!c%^!c!i3p!i#T%^#T#Z3p#Z;'S%^;'S;=`%o<%lO%^^3uYoWOy%^z!Q%^!Q![4e![!c%^!c!i4e!i#T%^#T#Z4e#Z;'S%^;'S;=`%o<%lO%^^4lYfUoWOy%^z!Q%^!Q![5[![!c%^!c!i5[!i#T%^#T#Z5[#Z;'S%^;'S;=`%o<%lO%^^5aYoWOy%^z!Q%^!Q![6P![!c%^!c!i6P!i#T%^#T#Z6P#Z;'S%^;'S;=`%o<%lO%^^6WSfUoWOy%^z;'S%^;'S;=`%o<%lO%^Y6gUOy%^z!_%^!_!`6y!`;'S%^;'S;=`%o<%lO%^Y7QSzQoWOy%^z;'S%^;'S;=`%o<%lO%^X7cSXPOy%^z;'S%^;'S;=`%o<%lO%^~7rWOY7oZw7owx.nx#O7o#O#P8[#P;'S7o;'S;=`9W<%lO7o~8_RO;'S7o;'S;=`8h;=`O7o~8kXOY7oZw7owx.nx#O7o#O#P8[#P;'S7o;'S;=`9W;=`<%l7o<%lO7o~9ZP;=`<%l7o_9cSbVOy%^z;'S%^;'S;=`%o<%lO%^~9tOa~_9{UUPjSOy%^z!_%^!_!`6y!`;'S%^;'S;=`%o<%lO%^_:fWjS!PPOy%^z!O%^!O!P;O!P!Q%^!Q![>T![;'S%^;'S;=`%o<%lO%^^;TUoWOy%^z!Q%^!Q![;g![;'S%^;'S;=`%o<%lO%^^;nYoW#[UOy%^z!Q%^!Q![;g![!g%^!g!h<^!h#X%^#X#Y<^#Y;'S%^;'S;=`%o<%lO%^^<cYoWOy%^z{%^{|=R|}%^}!O=R!O!Q%^!Q![=j![;'S%^;'S;=`%o<%lO%^^=WUoWOy%^z!Q%^!Q![=j![;'S%^;'S;=`%o<%lO%^^=qUoW#[UOy%^z!Q%^!Q![=j![;'S%^;'S;=`%o<%lO%^^>[[oW#[UOy%^z!O%^!O!P;g!P!Q%^!Q![>T![!g%^!g!h<^!h#X%^#X#Y<^#Y;'S%^;'S;=`%o<%lO%^_?VSpVOy%^z;'S%^;'S;=`%o<%lO%^^?hWjSOy%^z!O%^!O!P;O!P!Q%^!Q![>T![;'S%^;'S;=`%o<%lO%^_@VU#XPOy%^z!Q%^!Q![;g![;'S%^;'S;=`%o<%lO%^~@nTjSOy%^z{@}{;'S%^;'S;=`%o<%lO%^~ASUoWOy@}yzAfz{Bm{;'S@};'S;=`Co<%lO@}~AiTOzAfz{Ax{;'SAf;'S;=`Bg<%lOAf~A{VOzAfz{Ax{!PAf!P!QBb!Q;'SAf;'S;=`Bg<%lOAf~BgOR~~BjP;=`<%lAf~BrWoWOy@}yzAfz{Bm{!P@}!P!QC[!Q;'S@};'S;=`Co<%lO@}~CcSoWR~Oy%^z;'S%^;'S;=`%o<%lO%^~CrP;=`<%l@}^Cz[#[UOy%^z!O%^!O!P;g!P!Q%^!Q![>T![!g%^!g!h<^!h#X%^#X#Y<^#Y;'S%^;'S;=`%o<%lO%^XDuU]POy%^z![%^![!]EX!];'S%^;'S;=`%o<%lO%^XE`S^PoWOy%^z;'S%^;'S;=`%o<%lO%^_EqS!WVOy%^z;'S%^;'S;=`%o<%lO%^YFSSzQOy%^z;'S%^;'S;=`%o<%lO%^XFeU|POy%^z!`%^!`!aFw!a;'S%^;'S;=`%o<%lO%^XGOS|PoWOy%^z;'S%^;'S;=`%o<%lO%^XG_WOy%^z!c%^!c!}Gw!}#T%^#T#oGw#o;'S%^;'S;=`%o<%lO%^XHO[!YPoWOy%^z}%^}!OGw!O!Q%^!Q![Gw![!c%^!c!}Gw!}#T%^#T#oGw#o;'S%^;'S;=`%o<%lO%^XHySxPOy%^z;'S%^;'S;=`%o<%lO%^^I[SvUOy%^z;'S%^;'S;=`%o<%lO%^XIkUOy%^z#b%^#b#cI}#c;'S%^;'S;=`%o<%lO%^XJSUoWOy%^z#W%^#W#XJf#X;'S%^;'S;=`%o<%lO%^XJmS!`PoWOy%^z;'S%^;'S;=`%o<%lO%^XJ|UOy%^z#f%^#f#gJf#g;'S%^;'S;=`%o<%lO%^XKeS!RPOy%^z;'S%^;'S;=`%o<%lO%^_KvS!QVOy%^z;'S%^;'S;=`%o<%lO%^ZLXU!PPOy%^z!_%^!_!`6y!`;'S%^;'S;=`%o<%lO%^WLnP;=`<%l$}",
|
|
tokenizers: [descendant, unitToken, identifiers, 0, 1, 2, 3],
|
|
topRules: { "StyleSheet": [0, 4], "Styles": [1, 84] },
|
|
specialized: [{ term: 95, get: (value) => spec_callee[value] || -1 }, { term: 56, get: (value) => spec_AtKeyword[value] || -1 }, { term: 96, get: (value) => spec_identifier[value] || -1 }],
|
|
tokenPrec: 1123
|
|
});
|
|
|
|
// node_modules/@codemirror/lang-css/dist/index.js
|
|
var import_language4 = require("@codemirror/language");
|
|
var import_common = require("@lezer/common");
|
|
var _properties = null;
|
|
function properties() {
|
|
if (!_properties && typeof document == "object" && document.body) {
|
|
let { style } = document.body, names = [], seen = /* @__PURE__ */ new Set();
|
|
for (let prop in style)
|
|
if (prop != "cssText" && prop != "cssFloat") {
|
|
if (typeof style[prop] == "string") {
|
|
if (/[A-Z]/.test(prop))
|
|
prop = prop.replace(/[A-Z]/g, (ch) => "-" + ch.toLowerCase());
|
|
if (!seen.has(prop)) {
|
|
names.push(prop);
|
|
seen.add(prop);
|
|
}
|
|
}
|
|
}
|
|
_properties = names.sort().map((name) => ({ type: "property", label: name }));
|
|
}
|
|
return _properties || [];
|
|
}
|
|
var pseudoClasses = /* @__PURE__ */ [
|
|
"active",
|
|
"after",
|
|
"any-link",
|
|
"autofill",
|
|
"backdrop",
|
|
"before",
|
|
"checked",
|
|
"cue",
|
|
"default",
|
|
"defined",
|
|
"disabled",
|
|
"empty",
|
|
"enabled",
|
|
"file-selector-button",
|
|
"first",
|
|
"first-child",
|
|
"first-letter",
|
|
"first-line",
|
|
"first-of-type",
|
|
"focus",
|
|
"focus-visible",
|
|
"focus-within",
|
|
"fullscreen",
|
|
"has",
|
|
"host",
|
|
"host-context",
|
|
"hover",
|
|
"in-range",
|
|
"indeterminate",
|
|
"invalid",
|
|
"is",
|
|
"lang",
|
|
"last-child",
|
|
"last-of-type",
|
|
"left",
|
|
"link",
|
|
"marker",
|
|
"modal",
|
|
"not",
|
|
"nth-child",
|
|
"nth-last-child",
|
|
"nth-last-of-type",
|
|
"nth-of-type",
|
|
"only-child",
|
|
"only-of-type",
|
|
"optional",
|
|
"out-of-range",
|
|
"part",
|
|
"placeholder",
|
|
"placeholder-shown",
|
|
"read-only",
|
|
"read-write",
|
|
"required",
|
|
"right",
|
|
"root",
|
|
"scope",
|
|
"selection",
|
|
"slotted",
|
|
"target",
|
|
"target-text",
|
|
"valid",
|
|
"visited",
|
|
"where"
|
|
].map((name) => ({ type: "class", label: name }));
|
|
var values = /* @__PURE__ */ [
|
|
"above",
|
|
"absolute",
|
|
"activeborder",
|
|
"additive",
|
|
"activecaption",
|
|
"after-white-space",
|
|
"ahead",
|
|
"alias",
|
|
"all",
|
|
"all-scroll",
|
|
"alphabetic",
|
|
"alternate",
|
|
"always",
|
|
"antialiased",
|
|
"appworkspace",
|
|
"asterisks",
|
|
"attr",
|
|
"auto",
|
|
"auto-flow",
|
|
"avoid",
|
|
"avoid-column",
|
|
"avoid-page",
|
|
"avoid-region",
|
|
"axis-pan",
|
|
"background",
|
|
"backwards",
|
|
"baseline",
|
|
"below",
|
|
"bidi-override",
|
|
"blink",
|
|
"block",
|
|
"block-axis",
|
|
"bold",
|
|
"bolder",
|
|
"border",
|
|
"border-box",
|
|
"both",
|
|
"bottom",
|
|
"break",
|
|
"break-all",
|
|
"break-word",
|
|
"bullets",
|
|
"button",
|
|
"button-bevel",
|
|
"buttonface",
|
|
"buttonhighlight",
|
|
"buttonshadow",
|
|
"buttontext",
|
|
"calc",
|
|
"capitalize",
|
|
"caps-lock-indicator",
|
|
"caption",
|
|
"captiontext",
|
|
"caret",
|
|
"cell",
|
|
"center",
|
|
"checkbox",
|
|
"circle",
|
|
"cjk-decimal",
|
|
"clear",
|
|
"clip",
|
|
"close-quote",
|
|
"col-resize",
|
|
"collapse",
|
|
"color",
|
|
"color-burn",
|
|
"color-dodge",
|
|
"column",
|
|
"column-reverse",
|
|
"compact",
|
|
"condensed",
|
|
"contain",
|
|
"content",
|
|
"contents",
|
|
"content-box",
|
|
"context-menu",
|
|
"continuous",
|
|
"copy",
|
|
"counter",
|
|
"counters",
|
|
"cover",
|
|
"crop",
|
|
"cross",
|
|
"crosshair",
|
|
"currentcolor",
|
|
"cursive",
|
|
"cyclic",
|
|
"darken",
|
|
"dashed",
|
|
"decimal",
|
|
"decimal-leading-zero",
|
|
"default",
|
|
"default-button",
|
|
"dense",
|
|
"destination-atop",
|
|
"destination-in",
|
|
"destination-out",
|
|
"destination-over",
|
|
"difference",
|
|
"disc",
|
|
"discard",
|
|
"disclosure-closed",
|
|
"disclosure-open",
|
|
"document",
|
|
"dot-dash",
|
|
"dot-dot-dash",
|
|
"dotted",
|
|
"double",
|
|
"down",
|
|
"e-resize",
|
|
"ease",
|
|
"ease-in",
|
|
"ease-in-out",
|
|
"ease-out",
|
|
"element",
|
|
"ellipse",
|
|
"ellipsis",
|
|
"embed",
|
|
"end",
|
|
"ethiopic-abegede-gez",
|
|
"ethiopic-halehame-aa-er",
|
|
"ethiopic-halehame-gez",
|
|
"ew-resize",
|
|
"exclusion",
|
|
"expanded",
|
|
"extends",
|
|
"extra-condensed",
|
|
"extra-expanded",
|
|
"fantasy",
|
|
"fast",
|
|
"fill",
|
|
"fill-box",
|
|
"fixed",
|
|
"flat",
|
|
"flex",
|
|
"flex-end",
|
|
"flex-start",
|
|
"footnotes",
|
|
"forwards",
|
|
"from",
|
|
"geometricPrecision",
|
|
"graytext",
|
|
"grid",
|
|
"groove",
|
|
"hand",
|
|
"hard-light",
|
|
"help",
|
|
"hidden",
|
|
"hide",
|
|
"higher",
|
|
"highlight",
|
|
"highlighttext",
|
|
"horizontal",
|
|
"hsl",
|
|
"hsla",
|
|
"hue",
|
|
"icon",
|
|
"ignore",
|
|
"inactiveborder",
|
|
"inactivecaption",
|
|
"inactivecaptiontext",
|
|
"infinite",
|
|
"infobackground",
|
|
"infotext",
|
|
"inherit",
|
|
"initial",
|
|
"inline",
|
|
"inline-axis",
|
|
"inline-block",
|
|
"inline-flex",
|
|
"inline-grid",
|
|
"inline-table",
|
|
"inset",
|
|
"inside",
|
|
"intrinsic",
|
|
"invert",
|
|
"italic",
|
|
"justify",
|
|
"keep-all",
|
|
"landscape",
|
|
"large",
|
|
"larger",
|
|
"left",
|
|
"level",
|
|
"lighter",
|
|
"lighten",
|
|
"line-through",
|
|
"linear",
|
|
"linear-gradient",
|
|
"lines",
|
|
"list-item",
|
|
"listbox",
|
|
"listitem",
|
|
"local",
|
|
"logical",
|
|
"loud",
|
|
"lower",
|
|
"lower-hexadecimal",
|
|
"lower-latin",
|
|
"lower-norwegian",
|
|
"lowercase",
|
|
"ltr",
|
|
"luminosity",
|
|
"manipulation",
|
|
"match",
|
|
"matrix",
|
|
"matrix3d",
|
|
"medium",
|
|
"menu",
|
|
"menutext",
|
|
"message-box",
|
|
"middle",
|
|
"min-intrinsic",
|
|
"mix",
|
|
"monospace",
|
|
"move",
|
|
"multiple",
|
|
"multiple_mask_images",
|
|
"multiply",
|
|
"n-resize",
|
|
"narrower",
|
|
"ne-resize",
|
|
"nesw-resize",
|
|
"no-close-quote",
|
|
"no-drop",
|
|
"no-open-quote",
|
|
"no-repeat",
|
|
"none",
|
|
"normal",
|
|
"not-allowed",
|
|
"nowrap",
|
|
"ns-resize",
|
|
"numbers",
|
|
"numeric",
|
|
"nw-resize",
|
|
"nwse-resize",
|
|
"oblique",
|
|
"opacity",
|
|
"open-quote",
|
|
"optimizeLegibility",
|
|
"optimizeSpeed",
|
|
"outset",
|
|
"outside",
|
|
"outside-shape",
|
|
"overlay",
|
|
"overline",
|
|
"padding",
|
|
"padding-box",
|
|
"painted",
|
|
"page",
|
|
"paused",
|
|
"perspective",
|
|
"pinch-zoom",
|
|
"plus-darker",
|
|
"plus-lighter",
|
|
"pointer",
|
|
"polygon",
|
|
"portrait",
|
|
"pre",
|
|
"pre-line",
|
|
"pre-wrap",
|
|
"preserve-3d",
|
|
"progress",
|
|
"push-button",
|
|
"radial-gradient",
|
|
"radio",
|
|
"read-only",
|
|
"read-write",
|
|
"read-write-plaintext-only",
|
|
"rectangle",
|
|
"region",
|
|
"relative",
|
|
"repeat",
|
|
"repeating-linear-gradient",
|
|
"repeating-radial-gradient",
|
|
"repeat-x",
|
|
"repeat-y",
|
|
"reset",
|
|
"reverse",
|
|
"rgb",
|
|
"rgba",
|
|
"ridge",
|
|
"right",
|
|
"rotate",
|
|
"rotate3d",
|
|
"rotateX",
|
|
"rotateY",
|
|
"rotateZ",
|
|
"round",
|
|
"row",
|
|
"row-resize",
|
|
"row-reverse",
|
|
"rtl",
|
|
"run-in",
|
|
"running",
|
|
"s-resize",
|
|
"sans-serif",
|
|
"saturation",
|
|
"scale",
|
|
"scale3d",
|
|
"scaleX",
|
|
"scaleY",
|
|
"scaleZ",
|
|
"screen",
|
|
"scroll",
|
|
"scrollbar",
|
|
"scroll-position",
|
|
"se-resize",
|
|
"self-start",
|
|
"self-end",
|
|
"semi-condensed",
|
|
"semi-expanded",
|
|
"separate",
|
|
"serif",
|
|
"show",
|
|
"single",
|
|
"skew",
|
|
"skewX",
|
|
"skewY",
|
|
"skip-white-space",
|
|
"slide",
|
|
"slider-horizontal",
|
|
"slider-vertical",
|
|
"sliderthumb-horizontal",
|
|
"sliderthumb-vertical",
|
|
"slow",
|
|
"small",
|
|
"small-caps",
|
|
"small-caption",
|
|
"smaller",
|
|
"soft-light",
|
|
"solid",
|
|
"source-atop",
|
|
"source-in",
|
|
"source-out",
|
|
"source-over",
|
|
"space",
|
|
"space-around",
|
|
"space-between",
|
|
"space-evenly",
|
|
"spell-out",
|
|
"square",
|
|
"start",
|
|
"static",
|
|
"status-bar",
|
|
"stretch",
|
|
"stroke",
|
|
"stroke-box",
|
|
"sub",
|
|
"subpixel-antialiased",
|
|
"svg_masks",
|
|
"super",
|
|
"sw-resize",
|
|
"symbolic",
|
|
"symbols",
|
|
"system-ui",
|
|
"table",
|
|
"table-caption",
|
|
"table-cell",
|
|
"table-column",
|
|
"table-column-group",
|
|
"table-footer-group",
|
|
"table-header-group",
|
|
"table-row",
|
|
"table-row-group",
|
|
"text",
|
|
"text-bottom",
|
|
"text-top",
|
|
"textarea",
|
|
"textfield",
|
|
"thick",
|
|
"thin",
|
|
"threeddarkshadow",
|
|
"threedface",
|
|
"threedhighlight",
|
|
"threedlightshadow",
|
|
"threedshadow",
|
|
"to",
|
|
"top",
|
|
"transform",
|
|
"translate",
|
|
"translate3d",
|
|
"translateX",
|
|
"translateY",
|
|
"translateZ",
|
|
"transparent",
|
|
"ultra-condensed",
|
|
"ultra-expanded",
|
|
"underline",
|
|
"unidirectional-pan",
|
|
"unset",
|
|
"up",
|
|
"upper-latin",
|
|
"uppercase",
|
|
"url",
|
|
"var",
|
|
"vertical",
|
|
"vertical-text",
|
|
"view-box",
|
|
"visible",
|
|
"visibleFill",
|
|
"visiblePainted",
|
|
"visibleStroke",
|
|
"visual",
|
|
"w-resize",
|
|
"wait",
|
|
"wave",
|
|
"wider",
|
|
"window",
|
|
"windowframe",
|
|
"windowtext",
|
|
"words",
|
|
"wrap",
|
|
"wrap-reverse",
|
|
"x-large",
|
|
"x-small",
|
|
"xor",
|
|
"xx-large",
|
|
"xx-small"
|
|
].map((name) => ({ type: "keyword", label: name })).concat(/* @__PURE__ */ [
|
|
"aliceblue",
|
|
"antiquewhite",
|
|
"aqua",
|
|
"aquamarine",
|
|
"azure",
|
|
"beige",
|
|
"bisque",
|
|
"black",
|
|
"blanchedalmond",
|
|
"blue",
|
|
"blueviolet",
|
|
"brown",
|
|
"burlywood",
|
|
"cadetblue",
|
|
"chartreuse",
|
|
"chocolate",
|
|
"coral",
|
|
"cornflowerblue",
|
|
"cornsilk",
|
|
"crimson",
|
|
"cyan",
|
|
"darkblue",
|
|
"darkcyan",
|
|
"darkgoldenrod",
|
|
"darkgray",
|
|
"darkgreen",
|
|
"darkkhaki",
|
|
"darkmagenta",
|
|
"darkolivegreen",
|
|
"darkorange",
|
|
"darkorchid",
|
|
"darkred",
|
|
"darksalmon",
|
|
"darkseagreen",
|
|
"darkslateblue",
|
|
"darkslategray",
|
|
"darkturquoise",
|
|
"darkviolet",
|
|
"deeppink",
|
|
"deepskyblue",
|
|
"dimgray",
|
|
"dodgerblue",
|
|
"firebrick",
|
|
"floralwhite",
|
|
"forestgreen",
|
|
"fuchsia",
|
|
"gainsboro",
|
|
"ghostwhite",
|
|
"gold",
|
|
"goldenrod",
|
|
"gray",
|
|
"grey",
|
|
"green",
|
|
"greenyellow",
|
|
"honeydew",
|
|
"hotpink",
|
|
"indianred",
|
|
"indigo",
|
|
"ivory",
|
|
"khaki",
|
|
"lavender",
|
|
"lavenderblush",
|
|
"lawngreen",
|
|
"lemonchiffon",
|
|
"lightblue",
|
|
"lightcoral",
|
|
"lightcyan",
|
|
"lightgoldenrodyellow",
|
|
"lightgray",
|
|
"lightgreen",
|
|
"lightpink",
|
|
"lightsalmon",
|
|
"lightseagreen",
|
|
"lightskyblue",
|
|
"lightslategray",
|
|
"lightsteelblue",
|
|
"lightyellow",
|
|
"lime",
|
|
"limegreen",
|
|
"linen",
|
|
"magenta",
|
|
"maroon",
|
|
"mediumaquamarine",
|
|
"mediumblue",
|
|
"mediumorchid",
|
|
"mediumpurple",
|
|
"mediumseagreen",
|
|
"mediumslateblue",
|
|
"mediumspringgreen",
|
|
"mediumturquoise",
|
|
"mediumvioletred",
|
|
"midnightblue",
|
|
"mintcream",
|
|
"mistyrose",
|
|
"moccasin",
|
|
"navajowhite",
|
|
"navy",
|
|
"oldlace",
|
|
"olive",
|
|
"olivedrab",
|
|
"orange",
|
|
"orangered",
|
|
"orchid",
|
|
"palegoldenrod",
|
|
"palegreen",
|
|
"paleturquoise",
|
|
"palevioletred",
|
|
"papayawhip",
|
|
"peachpuff",
|
|
"peru",
|
|
"pink",
|
|
"plum",
|
|
"powderblue",
|
|
"purple",
|
|
"rebeccapurple",
|
|
"red",
|
|
"rosybrown",
|
|
"royalblue",
|
|
"saddlebrown",
|
|
"salmon",
|
|
"sandybrown",
|
|
"seagreen",
|
|
"seashell",
|
|
"sienna",
|
|
"silver",
|
|
"skyblue",
|
|
"slateblue",
|
|
"slategray",
|
|
"snow",
|
|
"springgreen",
|
|
"steelblue",
|
|
"tan",
|
|
"teal",
|
|
"thistle",
|
|
"tomato",
|
|
"turquoise",
|
|
"violet",
|
|
"wheat",
|
|
"white",
|
|
"whitesmoke",
|
|
"yellow",
|
|
"yellowgreen"
|
|
].map((name) => ({ type: "constant", label: name })));
|
|
var tags2 = /* @__PURE__ */ [
|
|
"a",
|
|
"abbr",
|
|
"address",
|
|
"article",
|
|
"aside",
|
|
"b",
|
|
"bdi",
|
|
"bdo",
|
|
"blockquote",
|
|
"body",
|
|
"br",
|
|
"button",
|
|
"canvas",
|
|
"caption",
|
|
"cite",
|
|
"code",
|
|
"col",
|
|
"colgroup",
|
|
"dd",
|
|
"del",
|
|
"details",
|
|
"dfn",
|
|
"dialog",
|
|
"div",
|
|
"dl",
|
|
"dt",
|
|
"em",
|
|
"figcaption",
|
|
"figure",
|
|
"footer",
|
|
"form",
|
|
"header",
|
|
"hgroup",
|
|
"h1",
|
|
"h2",
|
|
"h3",
|
|
"h4",
|
|
"h5",
|
|
"h6",
|
|
"hr",
|
|
"html",
|
|
"i",
|
|
"iframe",
|
|
"img",
|
|
"input",
|
|
"ins",
|
|
"kbd",
|
|
"label",
|
|
"legend",
|
|
"li",
|
|
"main",
|
|
"meter",
|
|
"nav",
|
|
"ol",
|
|
"output",
|
|
"p",
|
|
"pre",
|
|
"ruby",
|
|
"section",
|
|
"select",
|
|
"small",
|
|
"source",
|
|
"span",
|
|
"strong",
|
|
"sub",
|
|
"summary",
|
|
"sup",
|
|
"table",
|
|
"tbody",
|
|
"td",
|
|
"template",
|
|
"textarea",
|
|
"tfoot",
|
|
"th",
|
|
"thead",
|
|
"tr",
|
|
"u",
|
|
"ul"
|
|
].map((name) => ({ type: "type", label: name }));
|
|
var identifier2 = /^(\w[\w-]*|-\w[\w-]*|)$/;
|
|
var variable = /^-(-[\w-]*)?$/;
|
|
function isVarArg(node, doc) {
|
|
var _a;
|
|
if (node.name == "(" || node.type.isError)
|
|
node = node.parent || node;
|
|
if (node.name != "ArgList")
|
|
return false;
|
|
let callee2 = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.firstChild;
|
|
if ((callee2 === null || callee2 === void 0 ? void 0 : callee2.name) != "Callee")
|
|
return false;
|
|
return doc.sliceString(callee2.from, callee2.to) == "var";
|
|
}
|
|
var VariablesByNode = /* @__PURE__ */ new import_common.NodeWeakMap();
|
|
var declSelector = ["Declaration"];
|
|
function astTop(node) {
|
|
for (let cur2 = node; ; ) {
|
|
if (cur2.type.isTop)
|
|
return cur2;
|
|
if (!(cur2 = cur2.parent))
|
|
return node;
|
|
}
|
|
}
|
|
function variableNames(doc, node, isVariable) {
|
|
if (node.to - node.from > 4096) {
|
|
let known = VariablesByNode.get(node);
|
|
if (known)
|
|
return known;
|
|
let result = [], seen = /* @__PURE__ */ new Set(), cursor = node.cursor(import_common.IterMode.IncludeAnonymous);
|
|
if (cursor.firstChild())
|
|
do {
|
|
for (let option of variableNames(doc, cursor.node, isVariable))
|
|
if (!seen.has(option.label)) {
|
|
seen.add(option.label);
|
|
result.push(option);
|
|
}
|
|
} while (cursor.nextSibling());
|
|
VariablesByNode.set(node, result);
|
|
return result;
|
|
} else {
|
|
let result = [], seen = /* @__PURE__ */ new Set();
|
|
node.cursor().iterate((node2) => {
|
|
var _a;
|
|
if (isVariable(node2) && node2.matchContext(declSelector) && ((_a = node2.node.nextSibling) === null || _a === void 0 ? void 0 : _a.name) == ":") {
|
|
let name = doc.sliceString(node2.from, node2.to);
|
|
if (!seen.has(name)) {
|
|
seen.add(name);
|
|
result.push({ label: name, type: "variable" });
|
|
}
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
}
|
|
var defineCSSCompletionSource = (isVariable) => (context) => {
|
|
let { state, pos } = context, node = (0, import_language4.syntaxTree)(state).resolveInner(pos, -1);
|
|
let isDash = node.type.isError && node.from == node.to - 1 && state.doc.sliceString(node.from, node.to) == "-";
|
|
if (node.name == "PropertyName" || (isDash || node.name == "TagName") && /^(Block|Styles)$/.test(node.resolve(node.to).name))
|
|
return { from: node.from, options: properties(), validFor: identifier2 };
|
|
if (node.name == "ValueName")
|
|
return { from: node.from, options: values, validFor: identifier2 };
|
|
if (node.name == "PseudoClassName")
|
|
return { from: node.from, options: pseudoClasses, validFor: identifier2 };
|
|
if (isVariable(node) || (context.explicit || isDash) && isVarArg(node, state.doc))
|
|
return {
|
|
from: isVariable(node) || isDash ? node.from : pos,
|
|
options: variableNames(state.doc, astTop(node), isVariable),
|
|
validFor: variable
|
|
};
|
|
if (node.name == "TagName") {
|
|
for (let { parent } = node; parent; parent = parent.parent)
|
|
if (parent.name == "Block")
|
|
return { from: node.from, options: properties(), validFor: identifier2 };
|
|
return { from: node.from, options: tags2, validFor: identifier2 };
|
|
}
|
|
if (!context.explicit)
|
|
return null;
|
|
let above = node.resolve(pos), before = above.childBefore(pos);
|
|
if (before && before.name == ":" && above.name == "PseudoClassSelector")
|
|
return { from: pos, options: pseudoClasses, validFor: identifier2 };
|
|
if (before && before.name == ":" && above.name == "Declaration" || above.name == "ArgList")
|
|
return { from: pos, options: values, validFor: identifier2 };
|
|
if (above.name == "Block" || above.name == "Styles")
|
|
return { from: pos, options: properties(), validFor: identifier2 };
|
|
return null;
|
|
};
|
|
var cssLanguage = /* @__PURE__ */ import_language4.LRLanguage.define({
|
|
name: "css",
|
|
parser: /* @__PURE__ */ parser.configure({
|
|
props: [
|
|
/* @__PURE__ */ import_language4.indentNodeProp.add({
|
|
Declaration: /* @__PURE__ */ (0, import_language4.continuedIndent)()
|
|
}),
|
|
/* @__PURE__ */ import_language4.foldNodeProp.add({
|
|
Block: import_language4.foldInside
|
|
})
|
|
]
|
|
}),
|
|
languageData: {
|
|
commentTokens: { block: { open: "/*", close: "*/" } },
|
|
indentOnInput: /^\s*\}$/,
|
|
wordChars: "-"
|
|
}
|
|
});
|
|
|
|
// src/codemirror-extensions/reconfigured-css.ts
|
|
var import_language5 = require("@codemirror/language");
|
|
var import_common2 = require("@lezer/common");
|
|
var ClassSelectorCtx = ["ClassSelector"];
|
|
var IdSelectorCtx = ["IdSelector"];
|
|
var SelectorsByNode = {
|
|
["class" /* CLASS */]: new import_common2.NodeWeakMap(),
|
|
["id" /* ID */]: new import_common2.NodeWeakMap()
|
|
};
|
|
var Identifier = /^(\w[\w-]*|-\w[\w-]*|)$/;
|
|
function getASTTop(node) {
|
|
for (let cur2 = node; ; ) {
|
|
if (cur2.type.isTop) return cur2;
|
|
if (!(cur2 = cur2.parent)) return node;
|
|
}
|
|
}
|
|
function isSelectorIdentifier(node) {
|
|
if (node.name == "ClassName") return "class" /* CLASS */;
|
|
if (node.name == "IdName") return "id" /* ID */;
|
|
return false;
|
|
}
|
|
function touchSelection(selection, range) {
|
|
return selection.ranges.findIndex(
|
|
(selRange) => selRange.from <= range.to && selRange.to >= range.from
|
|
) >= 0;
|
|
}
|
|
function getSelectorNames(state, node, type) {
|
|
if (node.to - node.from > 4096) {
|
|
const known = SelectorsByNode[type].get(node);
|
|
if (known) return known;
|
|
const result = [], seen = /* @__PURE__ */ new Set(), cursor = node.cursor(import_common2.IterMode.IncludeAnonymous);
|
|
if (cursor.firstChild())
|
|
do {
|
|
for (const option of getSelectorNames(state, cursor.node, type))
|
|
if (!seen.has(option.label)) {
|
|
seen.add(option.label);
|
|
result.push(option);
|
|
}
|
|
} while (cursor.nextSibling());
|
|
SelectorsByNode[type].set(node, result);
|
|
return result;
|
|
} else {
|
|
const result = [], seen = /* @__PURE__ */ new Set(), { doc, selection } = state;
|
|
node.cursor().iterate((node2) => {
|
|
if (type == isSelectorIdentifier(node2) && !touchSelection(selection, node2)) {
|
|
const name = doc.sliceString(node2.from, node2.to);
|
|
if (!seen.has(name)) {
|
|
seen.add(name);
|
|
result.push({ label: name, type: "variable" });
|
|
}
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
}
|
|
var cssCompletionSource = (context) => {
|
|
let result = defineCSSCompletionSource(
|
|
(node) => node.name == "VariableName"
|
|
)(context);
|
|
if (!result) {
|
|
const { state, pos } = context, node = (0, import_language5.syntaxTree)(state).resolveInner(pos, -1);
|
|
if (node.matchContext(ClassSelectorCtx)) {
|
|
const isDot = node.name == ".";
|
|
result = {
|
|
from: isDot ? node.to : node.from,
|
|
options: getSelectorNames(
|
|
state,
|
|
getASTTop(node),
|
|
"class" /* CLASS */
|
|
),
|
|
validFor: Identifier
|
|
};
|
|
} else if (node.matchContext(IdSelectorCtx)) {
|
|
const isHash = node.name == "#";
|
|
result = {
|
|
from: isHash ? node.to : node.from,
|
|
options: getSelectorNames(
|
|
state,
|
|
getASTTop(node),
|
|
"id" /* ID */
|
|
),
|
|
validFor: Identifier
|
|
};
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
var css = () => new import_language5.LanguageSupport(
|
|
cssLanguage,
|
|
cssLanguage.data.of({ autocomplete: cssCompletionSource })
|
|
);
|
|
|
|
// src/codemirror-extensions/highlight-active-line.ts
|
|
var import_view4 = require("@codemirror/view");
|
|
function highlightActiveLine() {
|
|
return activeLineLayer;
|
|
}
|
|
var activeLineLayer = (0, import_view4.layer)({
|
|
above: false,
|
|
class: "cm-activeLineLayer",
|
|
markers(view) {
|
|
const selection = view.state.selection, markers = [], paddingTop = view.documentPadding.top, { width, left: contentLeft } = view.contentDOM.getBoundingClientRect(), { left: scrollerLeft } = view.scrollDOM.getBoundingClientRect();
|
|
for (const range of selection.ranges) {
|
|
const { top, height } = view.lineBlockAt(range.head), layer2 = new import_view4.RectangleMarker(
|
|
"cm-activeLine",
|
|
contentLeft - scrollerLeft,
|
|
top + paddingTop,
|
|
width,
|
|
height
|
|
);
|
|
markers.push(layer2);
|
|
}
|
|
return markers;
|
|
},
|
|
update(update) {
|
|
return update.docChanged || update.selectionSet || update.viewportChanged || update.geometryChanged;
|
|
}
|
|
});
|
|
|
|
// src/codemirror-extensions/basic-extensions.ts
|
|
var basicExtensions = [
|
|
import_view5.keymap.of([
|
|
...closeBracketsKeymap,
|
|
// "{|}" -> backspace -> "|"
|
|
...import_commands2.defaultKeymap,
|
|
...import_search2.searchKeymap,
|
|
...import_commands2.historyKeymap,
|
|
import_commands2.indentWithTab,
|
|
...import_language6.foldKeymap,
|
|
...completionKeymap,
|
|
...import_lint.lintKeymap
|
|
]),
|
|
css(),
|
|
(0, import_view5.lineNumbers)(),
|
|
(0, import_language6.foldGutter)(),
|
|
(0, import_view5.highlightActiveLineGutter)(),
|
|
(0, import_view5.dropCursor)(),
|
|
(0, import_view5.drawSelection)({ drawRangeCursor: true }),
|
|
import_state3.EditorState.allowMultipleSelections.of(true),
|
|
highlightActiveLine(),
|
|
(0, import_language6.indentOnInput)(),
|
|
(0, import_language6.bracketMatching)(),
|
|
autocompletion(),
|
|
closeBrackets(),
|
|
(0, import_search2.highlightSelectionMatches)(),
|
|
obsidian
|
|
].filter((ext) => ext);
|
|
|
|
// src/views/CssEditorView.ts
|
|
var import_commands3 = require("@codemirror/commands");
|
|
|
|
// src/codemirror-extensions/compartments.ts
|
|
var import_state5 = require("@codemirror/state");
|
|
|
|
// src/codemirror-extensions/relative-line-numbers.ts
|
|
var import_view6 = require("@codemirror/view");
|
|
var import_state4 = require("@codemirror/state");
|
|
var import_language7 = require("@codemirror/language");
|
|
var relativeLineNumberGutter = new import_state4.Compartment();
|
|
var charLengthCache = /* @__PURE__ */ new WeakMap();
|
|
function relativeLineNumbersFormatter(lineNo, state) {
|
|
let charLength = charLengthCache.get(state);
|
|
if (!charLength) {
|
|
charLength = state.doc.lines.toString().length;
|
|
charLengthCache.set(state, charLength);
|
|
}
|
|
if (lineNo > state.doc.lines) {
|
|
return " ".padStart(charLength, " ");
|
|
}
|
|
const selection = state.selection.asSingle();
|
|
if (!selection.ranges[0]) {
|
|
return " ".padStart(charLength, " ");
|
|
}
|
|
const cursorLine = state.doc.lineAt(selection.ranges[0].to).number;
|
|
const folds = (0, import_language7.foldedRanges)(state);
|
|
if (lineNo === cursorLine) {
|
|
return lineNo.toString().padStart(charLength, " ");
|
|
}
|
|
const start = Math.min(cursorLine, lineNo);
|
|
const stop = Math.max(cursorLine, lineNo);
|
|
let foldedCount = 0;
|
|
folds.between(state.doc.line(start).from, state.doc.line(stop).to, () => {
|
|
foldedCount++;
|
|
});
|
|
const distance = Math.abs(cursorLine - lineNo) - foldedCount;
|
|
return distance.toString().padStart(charLength, " ");
|
|
}
|
|
function absoluteLineNumbers(lineNo, state) {
|
|
let charLength = charLengthCache.get(state);
|
|
if (!charLength) {
|
|
charLength = state.doc.lines.toString().length;
|
|
charLengthCache.set(state, charLength);
|
|
}
|
|
return lineNo.toString().padStart(charLength, " ");
|
|
}
|
|
|
|
// src/codemirror-extensions/compartments.ts
|
|
var lineWrap = new import_state5.Compartment();
|
|
var indentSize = new import_state5.Compartment();
|
|
var historyCompartment = new import_state5.Compartment();
|
|
|
|
// src/views/CssEditorView.ts
|
|
var import_language9 = require("@codemirror/language");
|
|
|
|
// src/modals/CssSnippetRenameModal.ts
|
|
var import_obsidian3 = require("obsidian");
|
|
|
|
// src/utils/handle-error.ts
|
|
var import_obsidian2 = require("obsidian");
|
|
function handleError(err, fallbackMessage = "Action failed. Reason unknown.") {
|
|
let message = fallbackMessage;
|
|
if (err instanceof Error) {
|
|
message = err.message;
|
|
}
|
|
new import_obsidian2.Notice(message);
|
|
console.error(`[CSS Editor]: ${message}`);
|
|
console.error(err);
|
|
}
|
|
|
|
// src/modals/CssSnippetRenameModal.ts
|
|
var CssSnippetRenameModal = class extends import_obsidian3.Modal {
|
|
constructor(app, file) {
|
|
super(app);
|
|
this.value = "";
|
|
this.file = file;
|
|
}
|
|
async onOpen() {
|
|
await super.onOpen();
|
|
this.titleEl.setText("Rename CSS snippet");
|
|
this.containerEl.addClass("css-editor-rename-modal");
|
|
this.buildForm();
|
|
}
|
|
buildForm() {
|
|
const textInput = new import_obsidian3.TextComponent(this.contentEl);
|
|
textInput.setPlaceholder("CSS snippet file name (ex: snippet.css)");
|
|
textInput.setValue(this.file.basename);
|
|
textInput.onChange((val) => this.value = val);
|
|
textInput.inputEl.addEventListener("keydown", (evt) => {
|
|
this.handleKeydown(evt);
|
|
});
|
|
const buttonContainer = this.contentEl.createDiv(
|
|
"modal-button-container"
|
|
);
|
|
new import_obsidian3.ButtonComponent(buttonContainer).setButtonText("Save").setCta().onClick(() => this.save());
|
|
new import_obsidian3.ButtonComponent(buttonContainer).setButtonText("Cancel").onClick(() => this.close());
|
|
}
|
|
handleKeydown(evt) {
|
|
if (evt.key === "Escape") {
|
|
this.close();
|
|
} else if (evt.key === "Enter") {
|
|
this.save().catch((err) => {
|
|
handleError(err, "Failed to rename CSS file.");
|
|
});
|
|
}
|
|
}
|
|
async save() {
|
|
try {
|
|
await renameSnippetFile(this.app, this.file, this.value);
|
|
this.close();
|
|
} catch (err) {
|
|
handleError(err, "Failed to rename CSS file.");
|
|
}
|
|
}
|
|
};
|
|
|
|
// src/obsidian/view-helpers.ts
|
|
function focusAndSelectElement(el) {
|
|
el.focus({ preventScroll: true });
|
|
const range = document.createRange();
|
|
range.selectNodeContents(el);
|
|
const selection = getSelection();
|
|
if (selection) {
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
}
|
|
}
|
|
|
|
// src/codemirror-extensions/color-picker.ts
|
|
var import_view7 = require("@codemirror/view");
|
|
var import_obsidian4 = require("obsidian");
|
|
|
|
// node_modules/color-string/node_modules/color-name/index.js
|
|
var color_name_default = {
|
|
aliceblue: [240, 248, 255],
|
|
antiquewhite: [250, 235, 215],
|
|
aqua: [0, 255, 255],
|
|
aquamarine: [127, 255, 212],
|
|
azure: [240, 255, 255],
|
|
beige: [245, 245, 220],
|
|
bisque: [255, 228, 196],
|
|
black: [0, 0, 0],
|
|
blanchedalmond: [255, 235, 205],
|
|
blue: [0, 0, 255],
|
|
blueviolet: [138, 43, 226],
|
|
brown: [165, 42, 42],
|
|
burlywood: [222, 184, 135],
|
|
cadetblue: [95, 158, 160],
|
|
chartreuse: [127, 255, 0],
|
|
chocolate: [210, 105, 30],
|
|
coral: [255, 127, 80],
|
|
cornflowerblue: [100, 149, 237],
|
|
cornsilk: [255, 248, 220],
|
|
crimson: [220, 20, 60],
|
|
cyan: [0, 255, 255],
|
|
darkblue: [0, 0, 139],
|
|
darkcyan: [0, 139, 139],
|
|
darkgoldenrod: [184, 134, 11],
|
|
darkgray: [169, 169, 169],
|
|
darkgreen: [0, 100, 0],
|
|
darkgrey: [169, 169, 169],
|
|
darkkhaki: [189, 183, 107],
|
|
darkmagenta: [139, 0, 139],
|
|
darkolivegreen: [85, 107, 47],
|
|
darkorange: [255, 140, 0],
|
|
darkorchid: [153, 50, 204],
|
|
darkred: [139, 0, 0],
|
|
darksalmon: [233, 150, 122],
|
|
darkseagreen: [143, 188, 143],
|
|
darkslateblue: [72, 61, 139],
|
|
darkslategray: [47, 79, 79],
|
|
darkslategrey: [47, 79, 79],
|
|
darkturquoise: [0, 206, 209],
|
|
darkviolet: [148, 0, 211],
|
|
deeppink: [255, 20, 147],
|
|
deepskyblue: [0, 191, 255],
|
|
dimgray: [105, 105, 105],
|
|
dimgrey: [105, 105, 105],
|
|
dodgerblue: [30, 144, 255],
|
|
firebrick: [178, 34, 34],
|
|
floralwhite: [255, 250, 240],
|
|
forestgreen: [34, 139, 34],
|
|
fuchsia: [255, 0, 255],
|
|
gainsboro: [220, 220, 220],
|
|
ghostwhite: [248, 248, 255],
|
|
gold: [255, 215, 0],
|
|
goldenrod: [218, 165, 32],
|
|
gray: [128, 128, 128],
|
|
green: [0, 128, 0],
|
|
greenyellow: [173, 255, 47],
|
|
grey: [128, 128, 128],
|
|
honeydew: [240, 255, 240],
|
|
hotpink: [255, 105, 180],
|
|
indianred: [205, 92, 92],
|
|
indigo: [75, 0, 130],
|
|
ivory: [255, 255, 240],
|
|
khaki: [240, 230, 140],
|
|
lavender: [230, 230, 250],
|
|
lavenderblush: [255, 240, 245],
|
|
lawngreen: [124, 252, 0],
|
|
lemonchiffon: [255, 250, 205],
|
|
lightblue: [173, 216, 230],
|
|
lightcoral: [240, 128, 128],
|
|
lightcyan: [224, 255, 255],
|
|
lightgoldenrodyellow: [250, 250, 210],
|
|
lightgray: [211, 211, 211],
|
|
lightgreen: [144, 238, 144],
|
|
lightgrey: [211, 211, 211],
|
|
lightpink: [255, 182, 193],
|
|
lightsalmon: [255, 160, 122],
|
|
lightseagreen: [32, 178, 170],
|
|
lightskyblue: [135, 206, 250],
|
|
lightslategray: [119, 136, 153],
|
|
lightslategrey: [119, 136, 153],
|
|
lightsteelblue: [176, 196, 222],
|
|
lightyellow: [255, 255, 224],
|
|
lime: [0, 255, 0],
|
|
limegreen: [50, 205, 50],
|
|
linen: [250, 240, 230],
|
|
magenta: [255, 0, 255],
|
|
maroon: [128, 0, 0],
|
|
mediumaquamarine: [102, 205, 170],
|
|
mediumblue: [0, 0, 205],
|
|
mediumorchid: [186, 85, 211],
|
|
mediumpurple: [147, 112, 219],
|
|
mediumseagreen: [60, 179, 113],
|
|
mediumslateblue: [123, 104, 238],
|
|
mediumspringgreen: [0, 250, 154],
|
|
mediumturquoise: [72, 209, 204],
|
|
mediumvioletred: [199, 21, 133],
|
|
midnightblue: [25, 25, 112],
|
|
mintcream: [245, 255, 250],
|
|
mistyrose: [255, 228, 225],
|
|
moccasin: [255, 228, 181],
|
|
navajowhite: [255, 222, 173],
|
|
navy: [0, 0, 128],
|
|
oldlace: [253, 245, 230],
|
|
olive: [128, 128, 0],
|
|
olivedrab: [107, 142, 35],
|
|
orange: [255, 165, 0],
|
|
orangered: [255, 69, 0],
|
|
orchid: [218, 112, 214],
|
|
palegoldenrod: [238, 232, 170],
|
|
palegreen: [152, 251, 152],
|
|
paleturquoise: [175, 238, 238],
|
|
palevioletred: [219, 112, 147],
|
|
papayawhip: [255, 239, 213],
|
|
peachpuff: [255, 218, 185],
|
|
peru: [205, 133, 63],
|
|
pink: [255, 192, 203],
|
|
plum: [221, 160, 221],
|
|
powderblue: [176, 224, 230],
|
|
purple: [128, 0, 128],
|
|
rebeccapurple: [102, 51, 153],
|
|
red: [255, 0, 0],
|
|
rosybrown: [188, 143, 143],
|
|
royalblue: [65, 105, 225],
|
|
saddlebrown: [139, 69, 19],
|
|
salmon: [250, 128, 114],
|
|
sandybrown: [244, 164, 96],
|
|
seagreen: [46, 139, 87],
|
|
seashell: [255, 245, 238],
|
|
sienna: [160, 82, 45],
|
|
silver: [192, 192, 192],
|
|
skyblue: [135, 206, 235],
|
|
slateblue: [106, 90, 205],
|
|
slategray: [112, 128, 144],
|
|
slategrey: [112, 128, 144],
|
|
snow: [255, 250, 250],
|
|
springgreen: [0, 255, 127],
|
|
steelblue: [70, 130, 180],
|
|
tan: [210, 180, 140],
|
|
teal: [0, 128, 128],
|
|
thistle: [216, 191, 216],
|
|
tomato: [255, 99, 71],
|
|
turquoise: [64, 224, 208],
|
|
violet: [238, 130, 238],
|
|
wheat: [245, 222, 179],
|
|
white: [255, 255, 255],
|
|
whitesmoke: [245, 245, 245],
|
|
yellow: [255, 255, 0],
|
|
yellowgreen: [154, 205, 50]
|
|
};
|
|
|
|
// node_modules/color-string/index.js
|
|
var reverseNames = /* @__PURE__ */ Object.create(null);
|
|
for (const name in color_name_default) {
|
|
if (Object.hasOwn(color_name_default, name)) {
|
|
reverseNames[color_name_default[name]] = name;
|
|
}
|
|
}
|
|
var cs = {
|
|
to: {},
|
|
get: {}
|
|
};
|
|
cs.get = function(string) {
|
|
const prefix = string.slice(0, 3).toLowerCase();
|
|
let value;
|
|
let model;
|
|
switch (prefix) {
|
|
case "hsl": {
|
|
value = cs.get.hsl(string);
|
|
model = "hsl";
|
|
break;
|
|
}
|
|
case "hwb": {
|
|
value = cs.get.hwb(string);
|
|
model = "hwb";
|
|
break;
|
|
}
|
|
default: {
|
|
value = cs.get.rgb(string);
|
|
model = "rgb";
|
|
break;
|
|
}
|
|
}
|
|
if (!value) {
|
|
return null;
|
|
}
|
|
return { model, value };
|
|
};
|
|
cs.get.rgb = function(string) {
|
|
if (!string) {
|
|
return null;
|
|
}
|
|
const abbr = /^#([a-f\d]{3,4})$/i;
|
|
const hex = /^#([a-f\d]{6})([a-f\d]{2})?$/i;
|
|
const rgba = /^rgba?\(\s*([+-]?\d+)(?=[\s,])\s*(?:,\s*)?([+-]?\d+)(?=[\s,])\s*(?:,\s*)?([+-]?\d+)\s*(?:[,|/]\s*([+-]?[\d.]+)(%?)\s*)?\)$/;
|
|
const per = /^rgba?\(\s*([+-]?[\d.]+)%\s*,?\s*([+-]?[\d.]+)%\s*,?\s*([+-]?[\d.]+)%\s*(?:[,|/]\s*([+-]?[\d.]+)(%?)\s*)?\)$/;
|
|
const keyword = /^(\w+)$/;
|
|
let rgb = [0, 0, 0, 1];
|
|
let match;
|
|
let i;
|
|
let hexAlpha;
|
|
if (match = string.match(hex)) {
|
|
hexAlpha = match[2];
|
|
match = match[1];
|
|
for (i = 0; i < 3; i++) {
|
|
const i2 = i * 2;
|
|
rgb[i] = Number.parseInt(match.slice(i2, i2 + 2), 16);
|
|
}
|
|
if (hexAlpha) {
|
|
rgb[3] = Number.parseInt(hexAlpha, 16) / 255;
|
|
}
|
|
} else if (match = string.match(abbr)) {
|
|
match = match[1];
|
|
hexAlpha = match[3];
|
|
for (i = 0; i < 3; i++) {
|
|
rgb[i] = Number.parseInt(match[i] + match[i], 16);
|
|
}
|
|
if (hexAlpha) {
|
|
rgb[3] = Number.parseInt(hexAlpha + hexAlpha, 16) / 255;
|
|
}
|
|
} else if (match = string.match(rgba)) {
|
|
for (i = 0; i < 3; i++) {
|
|
rgb[i] = Number.parseInt(match[i + 1], 10);
|
|
}
|
|
if (match[4]) {
|
|
rgb[3] = match[5] ? Number.parseFloat(match[4]) * 0.01 : Number.parseFloat(match[4]);
|
|
}
|
|
} else if (match = string.match(per)) {
|
|
for (i = 0; i < 3; i++) {
|
|
rgb[i] = Math.round(Number.parseFloat(match[i + 1]) * 2.55);
|
|
}
|
|
if (match[4]) {
|
|
rgb[3] = match[5] ? Number.parseFloat(match[4]) * 0.01 : Number.parseFloat(match[4]);
|
|
}
|
|
} else if (match = string.match(keyword)) {
|
|
if (match[1] === "transparent") {
|
|
return [0, 0, 0, 0];
|
|
}
|
|
if (!Object.hasOwn(color_name_default, match[1])) {
|
|
return null;
|
|
}
|
|
rgb = color_name_default[match[1]];
|
|
rgb[3] = 1;
|
|
return rgb;
|
|
} else {
|
|
return null;
|
|
}
|
|
for (i = 0; i < 3; i++) {
|
|
rgb[i] = clamp(rgb[i], 0, 255);
|
|
}
|
|
rgb[3] = clamp(rgb[3], 0, 1);
|
|
return rgb;
|
|
};
|
|
cs.get.hsl = function(string) {
|
|
if (!string) {
|
|
return null;
|
|
}
|
|
const hsl = /^hsla?\(\s*([+-]?(?:\d{0,3}\.)?\d+)(?:deg)?\s*,?\s*([+-]?[\d.]+)%\s*,?\s*([+-]?[\d.]+)%\s*(?:[,|/]\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:[eE][+-]?\d+)?)\s*)?\)$/;
|
|
const match = string.match(hsl);
|
|
if (match) {
|
|
const alpha = Number.parseFloat(match[4]);
|
|
const h = (Number.parseFloat(match[1]) % 360 + 360) % 360;
|
|
const s = clamp(Number.parseFloat(match[2]), 0, 100);
|
|
const l = clamp(Number.parseFloat(match[3]), 0, 100);
|
|
const a = clamp(Number.isNaN(alpha) ? 1 : alpha, 0, 1);
|
|
return [h, s, l, a];
|
|
}
|
|
return null;
|
|
};
|
|
cs.get.hwb = function(string) {
|
|
if (!string) {
|
|
return null;
|
|
}
|
|
const hwb = /^hwb\(\s*([+-]?\d{0,3}(?:\.\d+)?)(?:deg)?\s*,\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*(?:,\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:[eE][+-]?\d+)?)\s*)?\)$/;
|
|
const match = string.match(hwb);
|
|
if (match) {
|
|
const alpha = Number.parseFloat(match[4]);
|
|
const h = (Number.parseFloat(match[1]) % 360 + 360) % 360;
|
|
const w = clamp(Number.parseFloat(match[2]), 0, 100);
|
|
const b = clamp(Number.parseFloat(match[3]), 0, 100);
|
|
const a = clamp(Number.isNaN(alpha) ? 1 : alpha, 0, 1);
|
|
return [h, w, b, a];
|
|
}
|
|
return null;
|
|
};
|
|
cs.to.hex = function(...rgba) {
|
|
return "#" + hexDouble(rgba[0]) + hexDouble(rgba[1]) + hexDouble(rgba[2]) + (rgba[3] < 1 ? hexDouble(Math.round(rgba[3] * 255)) : "");
|
|
};
|
|
cs.to.rgb = function(...rgba) {
|
|
return rgba.length < 4 || rgba[3] === 1 ? "rgb(" + Math.round(rgba[0]) + ", " + Math.round(rgba[1]) + ", " + Math.round(rgba[2]) + ")" : "rgba(" + Math.round(rgba[0]) + ", " + Math.round(rgba[1]) + ", " + Math.round(rgba[2]) + ", " + rgba[3] + ")";
|
|
};
|
|
cs.to.rgb.percent = function(...rgba) {
|
|
const r = Math.round(rgba[0] / 255 * 100);
|
|
const g = Math.round(rgba[1] / 255 * 100);
|
|
const b = Math.round(rgba[2] / 255 * 100);
|
|
return rgba.length < 4 || rgba[3] === 1 ? "rgb(" + r + "%, " + g + "%, " + b + "%)" : "rgba(" + r + "%, " + g + "%, " + b + "%, " + rgba[3] + ")";
|
|
};
|
|
cs.to.hsl = function(...hsla) {
|
|
return hsla.length < 4 || hsla[3] === 1 ? "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)" : "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " + hsla[3] + ")";
|
|
};
|
|
cs.to.hwb = function(...hwba) {
|
|
let a = "";
|
|
if (hwba.length >= 4 && hwba[3] !== 1) {
|
|
a = ", " + hwba[3];
|
|
}
|
|
return "hwb(" + hwba[0] + ", " + hwba[1] + "%, " + hwba[2] + "%" + a + ")";
|
|
};
|
|
cs.to.keyword = function(...rgb) {
|
|
return reverseNames[rgb.slice(0, 3)];
|
|
};
|
|
function clamp(number_, min, max) {
|
|
return Math.min(Math.max(min, number_), max);
|
|
}
|
|
function hexDouble(number_) {
|
|
const string_ = Math.round(number_).toString(16).toUpperCase();
|
|
return string_.length < 2 ? "0" + string_ : string_;
|
|
}
|
|
var color_string_default = cs;
|
|
|
|
// node_modules/color/node_modules/color-name/index.js
|
|
var color_name_default2 = {
|
|
aliceblue: [240, 248, 255],
|
|
antiquewhite: [250, 235, 215],
|
|
aqua: [0, 255, 255],
|
|
aquamarine: [127, 255, 212],
|
|
azure: [240, 255, 255],
|
|
beige: [245, 245, 220],
|
|
bisque: [255, 228, 196],
|
|
black: [0, 0, 0],
|
|
blanchedalmond: [255, 235, 205],
|
|
blue: [0, 0, 255],
|
|
blueviolet: [138, 43, 226],
|
|
brown: [165, 42, 42],
|
|
burlywood: [222, 184, 135],
|
|
cadetblue: [95, 158, 160],
|
|
chartreuse: [127, 255, 0],
|
|
chocolate: [210, 105, 30],
|
|
coral: [255, 127, 80],
|
|
cornflowerblue: [100, 149, 237],
|
|
cornsilk: [255, 248, 220],
|
|
crimson: [220, 20, 60],
|
|
cyan: [0, 255, 255],
|
|
darkblue: [0, 0, 139],
|
|
darkcyan: [0, 139, 139],
|
|
darkgoldenrod: [184, 134, 11],
|
|
darkgray: [169, 169, 169],
|
|
darkgreen: [0, 100, 0],
|
|
darkgrey: [169, 169, 169],
|
|
darkkhaki: [189, 183, 107],
|
|
darkmagenta: [139, 0, 139],
|
|
darkolivegreen: [85, 107, 47],
|
|
darkorange: [255, 140, 0],
|
|
darkorchid: [153, 50, 204],
|
|
darkred: [139, 0, 0],
|
|
darksalmon: [233, 150, 122],
|
|
darkseagreen: [143, 188, 143],
|
|
darkslateblue: [72, 61, 139],
|
|
darkslategray: [47, 79, 79],
|
|
darkslategrey: [47, 79, 79],
|
|
darkturquoise: [0, 206, 209],
|
|
darkviolet: [148, 0, 211],
|
|
deeppink: [255, 20, 147],
|
|
deepskyblue: [0, 191, 255],
|
|
dimgray: [105, 105, 105],
|
|
dimgrey: [105, 105, 105],
|
|
dodgerblue: [30, 144, 255],
|
|
firebrick: [178, 34, 34],
|
|
floralwhite: [255, 250, 240],
|
|
forestgreen: [34, 139, 34],
|
|
fuchsia: [255, 0, 255],
|
|
gainsboro: [220, 220, 220],
|
|
ghostwhite: [248, 248, 255],
|
|
gold: [255, 215, 0],
|
|
goldenrod: [218, 165, 32],
|
|
gray: [128, 128, 128],
|
|
green: [0, 128, 0],
|
|
greenyellow: [173, 255, 47],
|
|
grey: [128, 128, 128],
|
|
honeydew: [240, 255, 240],
|
|
hotpink: [255, 105, 180],
|
|
indianred: [205, 92, 92],
|
|
indigo: [75, 0, 130],
|
|
ivory: [255, 255, 240],
|
|
khaki: [240, 230, 140],
|
|
lavender: [230, 230, 250],
|
|
lavenderblush: [255, 240, 245],
|
|
lawngreen: [124, 252, 0],
|
|
lemonchiffon: [255, 250, 205],
|
|
lightblue: [173, 216, 230],
|
|
lightcoral: [240, 128, 128],
|
|
lightcyan: [224, 255, 255],
|
|
lightgoldenrodyellow: [250, 250, 210],
|
|
lightgray: [211, 211, 211],
|
|
lightgreen: [144, 238, 144],
|
|
lightgrey: [211, 211, 211],
|
|
lightpink: [255, 182, 193],
|
|
lightsalmon: [255, 160, 122],
|
|
lightseagreen: [32, 178, 170],
|
|
lightskyblue: [135, 206, 250],
|
|
lightslategray: [119, 136, 153],
|
|
lightslategrey: [119, 136, 153],
|
|
lightsteelblue: [176, 196, 222],
|
|
lightyellow: [255, 255, 224],
|
|
lime: [0, 255, 0],
|
|
limegreen: [50, 205, 50],
|
|
linen: [250, 240, 230],
|
|
magenta: [255, 0, 255],
|
|
maroon: [128, 0, 0],
|
|
mediumaquamarine: [102, 205, 170],
|
|
mediumblue: [0, 0, 205],
|
|
mediumorchid: [186, 85, 211],
|
|
mediumpurple: [147, 112, 219],
|
|
mediumseagreen: [60, 179, 113],
|
|
mediumslateblue: [123, 104, 238],
|
|
mediumspringgreen: [0, 250, 154],
|
|
mediumturquoise: [72, 209, 204],
|
|
mediumvioletred: [199, 21, 133],
|
|
midnightblue: [25, 25, 112],
|
|
mintcream: [245, 255, 250],
|
|
mistyrose: [255, 228, 225],
|
|
moccasin: [255, 228, 181],
|
|
navajowhite: [255, 222, 173],
|
|
navy: [0, 0, 128],
|
|
oldlace: [253, 245, 230],
|
|
olive: [128, 128, 0],
|
|
olivedrab: [107, 142, 35],
|
|
orange: [255, 165, 0],
|
|
orangered: [255, 69, 0],
|
|
orchid: [218, 112, 214],
|
|
palegoldenrod: [238, 232, 170],
|
|
palegreen: [152, 251, 152],
|
|
paleturquoise: [175, 238, 238],
|
|
palevioletred: [219, 112, 147],
|
|
papayawhip: [255, 239, 213],
|
|
peachpuff: [255, 218, 185],
|
|
peru: [205, 133, 63],
|
|
pink: [255, 192, 203],
|
|
plum: [221, 160, 221],
|
|
powderblue: [176, 224, 230],
|
|
purple: [128, 0, 128],
|
|
rebeccapurple: [102, 51, 153],
|
|
red: [255, 0, 0],
|
|
rosybrown: [188, 143, 143],
|
|
royalblue: [65, 105, 225],
|
|
saddlebrown: [139, 69, 19],
|
|
salmon: [250, 128, 114],
|
|
sandybrown: [244, 164, 96],
|
|
seagreen: [46, 139, 87],
|
|
seashell: [255, 245, 238],
|
|
sienna: [160, 82, 45],
|
|
silver: [192, 192, 192],
|
|
skyblue: [135, 206, 235],
|
|
slateblue: [106, 90, 205],
|
|
slategray: [112, 128, 144],
|
|
slategrey: [112, 128, 144],
|
|
snow: [255, 250, 250],
|
|
springgreen: [0, 255, 127],
|
|
steelblue: [70, 130, 180],
|
|
tan: [210, 180, 140],
|
|
teal: [0, 128, 128],
|
|
thistle: [216, 191, 216],
|
|
tomato: [255, 99, 71],
|
|
turquoise: [64, 224, 208],
|
|
violet: [238, 130, 238],
|
|
wheat: [245, 222, 179],
|
|
white: [255, 255, 255],
|
|
whitesmoke: [245, 245, 245],
|
|
yellow: [255, 255, 0],
|
|
yellowgreen: [154, 205, 50]
|
|
};
|
|
|
|
// node_modules/color/node_modules/color-convert/conversions.js
|
|
var reverseKeywords = {};
|
|
for (const key of Object.keys(color_name_default2)) {
|
|
reverseKeywords[color_name_default2[key]] = key;
|
|
}
|
|
var convert = {
|
|
rgb: { channels: 3, labels: "rgb" },
|
|
hsl: { channels: 3, labels: "hsl" },
|
|
hsv: { channels: 3, labels: "hsv" },
|
|
hwb: { channels: 3, labels: "hwb" },
|
|
cmyk: { channels: 4, labels: "cmyk" },
|
|
xyz: { channels: 3, labels: "xyz" },
|
|
lab: { channels: 3, labels: "lab" },
|
|
oklab: { channels: 3, labels: ["okl", "oka", "okb"] },
|
|
lch: { channels: 3, labels: "lch" },
|
|
oklch: { channels: 3, labels: ["okl", "okc", "okh"] },
|
|
hex: { channels: 1, labels: ["hex"] },
|
|
keyword: { channels: 1, labels: ["keyword"] },
|
|
ansi16: { channels: 1, labels: ["ansi16"] },
|
|
ansi256: { channels: 1, labels: ["ansi256"] },
|
|
hcg: { channels: 3, labels: ["h", "c", "g"] },
|
|
apple: { channels: 3, labels: ["r16", "g16", "b16"] },
|
|
gray: { channels: 1, labels: ["gray"] }
|
|
};
|
|
var conversions_default = convert;
|
|
var LAB_FT = (6 / 29) ** 3;
|
|
function srgbNonlinearTransform(c) {
|
|
const cc = c > 31308e-7 ? 1.055 * c ** (1 / 2.4) - 0.055 : c * 12.92;
|
|
return Math.min(Math.max(0, cc), 1);
|
|
}
|
|
function srgbNonlinearTransformInv(c) {
|
|
return c > 0.04045 ? ((c + 0.055) / 1.055) ** 2.4 : c / 12.92;
|
|
}
|
|
for (const model of Object.keys(convert)) {
|
|
if (!("channels" in convert[model])) {
|
|
throw new Error("missing channels property: " + model);
|
|
}
|
|
if (!("labels" in convert[model])) {
|
|
throw new Error("missing channel labels property: " + model);
|
|
}
|
|
if (convert[model].labels.length !== convert[model].channels) {
|
|
throw new Error("channel and label counts mismatch: " + model);
|
|
}
|
|
const { channels, labels } = convert[model];
|
|
delete convert[model].channels;
|
|
delete convert[model].labels;
|
|
Object.defineProperty(convert[model], "channels", { value: channels });
|
|
Object.defineProperty(convert[model], "labels", { value: labels });
|
|
}
|
|
convert.rgb.hsl = function(rgb) {
|
|
const r = rgb[0] / 255;
|
|
const g = rgb[1] / 255;
|
|
const b = rgb[2] / 255;
|
|
const min = Math.min(r, g, b);
|
|
const max = Math.max(r, g, b);
|
|
const delta = max - min;
|
|
let h;
|
|
let s;
|
|
switch (max) {
|
|
case min: {
|
|
h = 0;
|
|
break;
|
|
}
|
|
case r: {
|
|
h = (g - b) / delta;
|
|
break;
|
|
}
|
|
case g: {
|
|
h = 2 + (b - r) / delta;
|
|
break;
|
|
}
|
|
case b: {
|
|
h = 4 + (r - g) / delta;
|
|
break;
|
|
}
|
|
}
|
|
h = Math.min(h * 60, 360);
|
|
if (h < 0) {
|
|
h += 360;
|
|
}
|
|
const l = (min + max) / 2;
|
|
if (max === min) {
|
|
s = 0;
|
|
} else if (l <= 0.5) {
|
|
s = delta / (max + min);
|
|
} else {
|
|
s = delta / (2 - max - min);
|
|
}
|
|
return [h, s * 100, l * 100];
|
|
};
|
|
convert.rgb.hsv = function(rgb) {
|
|
let rdif;
|
|
let gdif;
|
|
let bdif;
|
|
let h;
|
|
let s;
|
|
const r = rgb[0] / 255;
|
|
const g = rgb[1] / 255;
|
|
const b = rgb[2] / 255;
|
|
const v = Math.max(r, g, b);
|
|
const diff = v - Math.min(r, g, b);
|
|
const diffc = function(c) {
|
|
return (v - c) / 6 / diff + 1 / 2;
|
|
};
|
|
if (diff === 0) {
|
|
h = 0;
|
|
s = 0;
|
|
} else {
|
|
s = diff / v;
|
|
rdif = diffc(r);
|
|
gdif = diffc(g);
|
|
bdif = diffc(b);
|
|
switch (v) {
|
|
case r: {
|
|
h = bdif - gdif;
|
|
break;
|
|
}
|
|
case g: {
|
|
h = 1 / 3 + rdif - bdif;
|
|
break;
|
|
}
|
|
case b: {
|
|
h = 2 / 3 + gdif - rdif;
|
|
break;
|
|
}
|
|
}
|
|
if (h < 0) {
|
|
h += 1;
|
|
} else if (h > 1) {
|
|
h -= 1;
|
|
}
|
|
}
|
|
return [
|
|
h * 360,
|
|
s * 100,
|
|
v * 100
|
|
];
|
|
};
|
|
convert.rgb.hwb = function(rgb) {
|
|
const r = rgb[0];
|
|
const g = rgb[1];
|
|
let b = rgb[2];
|
|
const h = convert.rgb.hsl(rgb)[0];
|
|
const w = 1 / 255 * Math.min(r, Math.min(g, b));
|
|
b = 1 - 1 / 255 * Math.max(r, Math.max(g, b));
|
|
return [h, w * 100, b * 100];
|
|
};
|
|
convert.rgb.oklab = function(rgb) {
|
|
const r = srgbNonlinearTransformInv(rgb[0] / 255);
|
|
const g = srgbNonlinearTransformInv(rgb[1] / 255);
|
|
const b = srgbNonlinearTransformInv(rgb[2] / 255);
|
|
const lp = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
|
const mp = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
|
const sp = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
|
const l = 0.2104542553 * lp + 0.793617785 * mp - 0.0040720468 * sp;
|
|
const aa = 1.9779984951 * lp - 2.428592205 * mp + 0.4505937099 * sp;
|
|
const bb = 0.0259040371 * lp + 0.7827717662 * mp - 0.808675766 * sp;
|
|
return [l * 100, aa * 100, bb * 100];
|
|
};
|
|
convert.rgb.cmyk = function(rgb) {
|
|
const r = rgb[0] / 255;
|
|
const g = rgb[1] / 255;
|
|
const b = rgb[2] / 255;
|
|
const k = Math.min(1 - r, 1 - g, 1 - b);
|
|
const c = (1 - r - k) / (1 - k) || 0;
|
|
const m = (1 - g - k) / (1 - k) || 0;
|
|
const y = (1 - b - k) / (1 - k) || 0;
|
|
return [c * 100, m * 100, y * 100, k * 100];
|
|
};
|
|
function comparativeDistance(x, y) {
|
|
return (x[0] - y[0]) ** 2 + (x[1] - y[1]) ** 2 + (x[2] - y[2]) ** 2;
|
|
}
|
|
convert.rgb.keyword = function(rgb) {
|
|
const reversed = reverseKeywords[rgb];
|
|
if (reversed) {
|
|
return reversed;
|
|
}
|
|
let currentClosestDistance = Number.POSITIVE_INFINITY;
|
|
let currentClosestKeyword;
|
|
for (const keyword of Object.keys(color_name_default2)) {
|
|
const value = color_name_default2[keyword];
|
|
const distance = comparativeDistance(rgb, value);
|
|
if (distance < currentClosestDistance) {
|
|
currentClosestDistance = distance;
|
|
currentClosestKeyword = keyword;
|
|
}
|
|
}
|
|
return currentClosestKeyword;
|
|
};
|
|
convert.keyword.rgb = function(keyword) {
|
|
return color_name_default2[keyword];
|
|
};
|
|
convert.rgb.xyz = function(rgb) {
|
|
const r = srgbNonlinearTransformInv(rgb[0] / 255);
|
|
const g = srgbNonlinearTransformInv(rgb[1] / 255);
|
|
const b = srgbNonlinearTransformInv(rgb[2] / 255);
|
|
const x = r * 0.4124564 + g * 0.3575761 + b * 0.1804375;
|
|
const y = r * 0.2126729 + g * 0.7151522 + b * 0.072175;
|
|
const z = r * 0.0193339 + g * 0.119192 + b * 0.9503041;
|
|
return [x * 100, y * 100, z * 100];
|
|
};
|
|
convert.rgb.lab = function(rgb) {
|
|
const xyz = convert.rgb.xyz(rgb);
|
|
let x = xyz[0];
|
|
let y = xyz[1];
|
|
let z = xyz[2];
|
|
x /= 95.047;
|
|
y /= 100;
|
|
z /= 108.883;
|
|
x = x > LAB_FT ? x ** (1 / 3) : 7.787 * x + 16 / 116;
|
|
y = y > LAB_FT ? y ** (1 / 3) : 7.787 * y + 16 / 116;
|
|
z = z > LAB_FT ? z ** (1 / 3) : 7.787 * z + 16 / 116;
|
|
const l = 116 * y - 16;
|
|
const a = 500 * (x - y);
|
|
const b = 200 * (y - z);
|
|
return [l, a, b];
|
|
};
|
|
convert.hsl.rgb = function(hsl) {
|
|
const h = hsl[0] / 360;
|
|
const s = hsl[1] / 100;
|
|
const l = hsl[2] / 100;
|
|
let t3;
|
|
let value;
|
|
if (s === 0) {
|
|
value = l * 255;
|
|
return [value, value, value];
|
|
}
|
|
const t2 = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
const t1 = 2 * l - t2;
|
|
const rgb = [0, 0, 0];
|
|
for (let i = 0; i < 3; i++) {
|
|
t3 = h + 1 / 3 * -(i - 1);
|
|
if (t3 < 0) {
|
|
t3++;
|
|
}
|
|
if (t3 > 1) {
|
|
t3--;
|
|
}
|
|
if (6 * t3 < 1) {
|
|
value = t1 + (t2 - t1) * 6 * t3;
|
|
} else if (2 * t3 < 1) {
|
|
value = t2;
|
|
} else if (3 * t3 < 2) {
|
|
value = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
|
|
} else {
|
|
value = t1;
|
|
}
|
|
rgb[i] = value * 255;
|
|
}
|
|
return rgb;
|
|
};
|
|
convert.hsl.hsv = function(hsl) {
|
|
const h = hsl[0];
|
|
let s = hsl[1] / 100;
|
|
let l = hsl[2] / 100;
|
|
let smin = s;
|
|
const lmin = Math.max(l, 0.01);
|
|
l *= 2;
|
|
s *= l <= 1 ? l : 2 - l;
|
|
smin *= lmin <= 1 ? lmin : 2 - lmin;
|
|
const v = (l + s) / 2;
|
|
const sv = l === 0 ? 2 * smin / (lmin + smin) : 2 * s / (l + s);
|
|
return [h, sv * 100, v * 100];
|
|
};
|
|
convert.hsv.rgb = function(hsv) {
|
|
const h = hsv[0] / 60;
|
|
const s = hsv[1] / 100;
|
|
let v = hsv[2] / 100;
|
|
const hi = Math.floor(h) % 6;
|
|
const f = h - Math.floor(h);
|
|
const p = 255 * v * (1 - s);
|
|
const q = 255 * v * (1 - s * f);
|
|
const t2 = 255 * v * (1 - s * (1 - f));
|
|
v *= 255;
|
|
switch (hi) {
|
|
case 0: {
|
|
return [v, t2, p];
|
|
}
|
|
case 1: {
|
|
return [q, v, p];
|
|
}
|
|
case 2: {
|
|
return [p, v, t2];
|
|
}
|
|
case 3: {
|
|
return [p, q, v];
|
|
}
|
|
case 4: {
|
|
return [t2, p, v];
|
|
}
|
|
case 5: {
|
|
return [v, p, q];
|
|
}
|
|
}
|
|
};
|
|
convert.hsv.hsl = function(hsv) {
|
|
const h = hsv[0];
|
|
const s = hsv[1] / 100;
|
|
const v = hsv[2] / 100;
|
|
const vmin = Math.max(v, 0.01);
|
|
let sl;
|
|
let l;
|
|
l = (2 - s) * v;
|
|
const lmin = (2 - s) * vmin;
|
|
sl = s * vmin;
|
|
sl /= lmin <= 1 ? lmin : 2 - lmin;
|
|
sl = sl || 0;
|
|
l /= 2;
|
|
return [h, sl * 100, l * 100];
|
|
};
|
|
convert.hwb.rgb = function(hwb) {
|
|
const h = hwb[0] / 360;
|
|
let wh = hwb[1] / 100;
|
|
let bl = hwb[2] / 100;
|
|
const ratio = wh + bl;
|
|
let f;
|
|
if (ratio > 1) {
|
|
wh /= ratio;
|
|
bl /= ratio;
|
|
}
|
|
const i = Math.floor(6 * h);
|
|
const v = 1 - bl;
|
|
f = 6 * h - i;
|
|
if ((i & 1) !== 0) {
|
|
f = 1 - f;
|
|
}
|
|
const n = wh + f * (v - wh);
|
|
let r;
|
|
let g;
|
|
let b;
|
|
switch (i) {
|
|
default:
|
|
case 6:
|
|
case 0: {
|
|
r = v;
|
|
g = n;
|
|
b = wh;
|
|
break;
|
|
}
|
|
case 1: {
|
|
r = n;
|
|
g = v;
|
|
b = wh;
|
|
break;
|
|
}
|
|
case 2: {
|
|
r = wh;
|
|
g = v;
|
|
b = n;
|
|
break;
|
|
}
|
|
case 3: {
|
|
r = wh;
|
|
g = n;
|
|
b = v;
|
|
break;
|
|
}
|
|
case 4: {
|
|
r = n;
|
|
g = wh;
|
|
b = v;
|
|
break;
|
|
}
|
|
case 5: {
|
|
r = v;
|
|
g = wh;
|
|
b = n;
|
|
break;
|
|
}
|
|
}
|
|
return [r * 255, g * 255, b * 255];
|
|
};
|
|
convert.cmyk.rgb = function(cmyk) {
|
|
const c = cmyk[0] / 100;
|
|
const m = cmyk[1] / 100;
|
|
const y = cmyk[2] / 100;
|
|
const k = cmyk[3] / 100;
|
|
const r = 1 - Math.min(1, c * (1 - k) + k);
|
|
const g = 1 - Math.min(1, m * (1 - k) + k);
|
|
const b = 1 - Math.min(1, y * (1 - k) + k);
|
|
return [r * 255, g * 255, b * 255];
|
|
};
|
|
convert.xyz.rgb = function(xyz) {
|
|
const x = xyz[0] / 100;
|
|
const y = xyz[1] / 100;
|
|
const z = xyz[2] / 100;
|
|
let r;
|
|
let g;
|
|
let b;
|
|
r = x * 3.2404542 + y * -1.5371385 + z * -0.4985314;
|
|
g = x * -0.969266 + y * 1.8760108 + z * 0.041556;
|
|
b = x * 0.0556434 + y * -0.2040259 + z * 1.0572252;
|
|
r = srgbNonlinearTransform(r);
|
|
g = srgbNonlinearTransform(g);
|
|
b = srgbNonlinearTransform(b);
|
|
return [r * 255, g * 255, b * 255];
|
|
};
|
|
convert.xyz.lab = function(xyz) {
|
|
let x = xyz[0];
|
|
let y = xyz[1];
|
|
let z = xyz[2];
|
|
x /= 95.047;
|
|
y /= 100;
|
|
z /= 108.883;
|
|
x = x > LAB_FT ? x ** (1 / 3) : 7.787 * x + 16 / 116;
|
|
y = y > LAB_FT ? y ** (1 / 3) : 7.787 * y + 16 / 116;
|
|
z = z > LAB_FT ? z ** (1 / 3) : 7.787 * z + 16 / 116;
|
|
const l = 116 * y - 16;
|
|
const a = 500 * (x - y);
|
|
const b = 200 * (y - z);
|
|
return [l, a, b];
|
|
};
|
|
convert.xyz.oklab = function(xyz) {
|
|
const x = xyz[0] / 100;
|
|
const y = xyz[1] / 100;
|
|
const z = xyz[2] / 100;
|
|
const lp = Math.cbrt(0.8189330101 * x + 0.3618667424 * y - 0.1288597137 * z);
|
|
const mp = Math.cbrt(0.0329845436 * x + 0.9293118715 * y + 0.0361456387 * z);
|
|
const sp = Math.cbrt(0.0482003018 * x + 0.2643662691 * y + 0.633851707 * z);
|
|
const l = 0.2104542553 * lp + 0.793617785 * mp - 0.0040720468 * sp;
|
|
const a = 1.9779984951 * lp - 2.428592205 * mp + 0.4505937099 * sp;
|
|
const b = 0.0259040371 * lp + 0.7827717662 * mp - 0.808675766 * sp;
|
|
return [l * 100, a * 100, b * 100];
|
|
};
|
|
convert.oklab.oklch = function(oklab) {
|
|
return convert.lab.lch(oklab);
|
|
};
|
|
convert.oklab.xyz = function(oklab) {
|
|
const ll = oklab[0] / 100;
|
|
const a = oklab[1] / 100;
|
|
const b = oklab[2] / 100;
|
|
const l = (0.999999998 * ll + 0.396337792 * a + 0.215803758 * b) ** 3;
|
|
const m = (1.000000008 * ll - 0.105561342 * a - 0.063854175 * b) ** 3;
|
|
const s = (1.000000055 * ll - 0.089484182 * a - 1.291485538 * b) ** 3;
|
|
const x = 1.227013851 * l - 0.55779998 * m + 0.281256149 * s;
|
|
const y = -0.040580178 * l + 1.11225687 * m - 0.071676679 * s;
|
|
const z = -0.076381285 * l - 0.421481978 * m + 1.58616322 * s;
|
|
return [x * 100, y * 100, z * 100];
|
|
};
|
|
convert.oklab.rgb = function(oklab) {
|
|
const ll = oklab[0] / 100;
|
|
const aa = oklab[1] / 100;
|
|
const bb = oklab[2] / 100;
|
|
const l = (ll + 0.3963377774 * aa + 0.2158037573 * bb) ** 3;
|
|
const m = (ll - 0.1055613458 * aa - 0.0638541728 * bb) ** 3;
|
|
const s = (ll - 0.0894841775 * aa - 1.291485548 * bb) ** 3;
|
|
const r = srgbNonlinearTransform(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s);
|
|
const g = srgbNonlinearTransform(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s);
|
|
const b = srgbNonlinearTransform(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s);
|
|
return [r * 255, g * 255, b * 255];
|
|
};
|
|
convert.oklch.oklab = function(oklch) {
|
|
return convert.lch.lab(oklch);
|
|
};
|
|
convert.lab.xyz = function(lab) {
|
|
const l = lab[0];
|
|
const a = lab[1];
|
|
const b = lab[2];
|
|
let x;
|
|
let y;
|
|
let z;
|
|
y = (l + 16) / 116;
|
|
x = a / 500 + y;
|
|
z = y - b / 200;
|
|
const y2 = y ** 3;
|
|
const x2 = x ** 3;
|
|
const z2 = z ** 3;
|
|
y = y2 > LAB_FT ? y2 : (y - 16 / 116) / 7.787;
|
|
x = x2 > LAB_FT ? x2 : (x - 16 / 116) / 7.787;
|
|
z = z2 > LAB_FT ? z2 : (z - 16 / 116) / 7.787;
|
|
x *= 95.047;
|
|
y *= 100;
|
|
z *= 108.883;
|
|
return [x, y, z];
|
|
};
|
|
convert.lab.lch = function(lab) {
|
|
const l = lab[0];
|
|
const a = lab[1];
|
|
const b = lab[2];
|
|
let h;
|
|
const hr = Math.atan2(b, a);
|
|
h = hr * 360 / 2 / Math.PI;
|
|
if (h < 0) {
|
|
h += 360;
|
|
}
|
|
const c = Math.sqrt(a * a + b * b);
|
|
return [l, c, h];
|
|
};
|
|
convert.lch.lab = function(lch) {
|
|
const l = lch[0];
|
|
const c = lch[1];
|
|
const h = lch[2];
|
|
const hr = h / 360 * 2 * Math.PI;
|
|
const a = c * Math.cos(hr);
|
|
const b = c * Math.sin(hr);
|
|
return [l, a, b];
|
|
};
|
|
convert.rgb.ansi16 = function(args, saturation = null) {
|
|
const [r, g, b] = args;
|
|
let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation;
|
|
value = Math.round(value / 50);
|
|
if (value === 0) {
|
|
return 30;
|
|
}
|
|
let ansi = 30 + (Math.round(b / 255) << 2 | Math.round(g / 255) << 1 | Math.round(r / 255));
|
|
if (value === 2) {
|
|
ansi += 60;
|
|
}
|
|
return ansi;
|
|
};
|
|
convert.hsv.ansi16 = function(args) {
|
|
return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);
|
|
};
|
|
convert.rgb.ansi256 = function(args) {
|
|
const r = args[0];
|
|
const g = args[1];
|
|
const b = args[2];
|
|
if (r >> 4 === g >> 4 && g >> 4 === b >> 4) {
|
|
if (r < 8) {
|
|
return 16;
|
|
}
|
|
if (r > 248) {
|
|
return 231;
|
|
}
|
|
return Math.round((r - 8) / 247 * 24) + 232;
|
|
}
|
|
const ansi = 16 + 36 * Math.round(r / 255 * 5) + 6 * Math.round(g / 255 * 5) + Math.round(b / 255 * 5);
|
|
return ansi;
|
|
};
|
|
convert.ansi16.rgb = function(args) {
|
|
args = args[0];
|
|
let color = args % 10;
|
|
if (color === 0 || color === 7) {
|
|
if (args > 50) {
|
|
color += 3.5;
|
|
}
|
|
color = color / 10.5 * 255;
|
|
return [color, color, color];
|
|
}
|
|
const mult = (Math.trunc(args > 50) + 1) * 0.5;
|
|
const r = (color & 1) * mult * 255;
|
|
const g = (color >> 1 & 1) * mult * 255;
|
|
const b = (color >> 2 & 1) * mult * 255;
|
|
return [r, g, b];
|
|
};
|
|
convert.ansi256.rgb = function(args) {
|
|
args = args[0];
|
|
if (args >= 232) {
|
|
const c = (args - 232) * 10 + 8;
|
|
return [c, c, c];
|
|
}
|
|
args -= 16;
|
|
let rem;
|
|
const r = Math.floor(args / 36) / 5 * 255;
|
|
const g = Math.floor((rem = args % 36) / 6) / 5 * 255;
|
|
const b = rem % 6 / 5 * 255;
|
|
return [r, g, b];
|
|
};
|
|
convert.rgb.hex = function(args) {
|
|
const integer = ((Math.round(args[0]) & 255) << 16) + ((Math.round(args[1]) & 255) << 8) + (Math.round(args[2]) & 255);
|
|
const string = integer.toString(16).toUpperCase();
|
|
return "000000".slice(string.length) + string;
|
|
};
|
|
convert.hex.rgb = function(args) {
|
|
const match = args.toString(16).match(/[a-f\d]{6}|[a-f\d]{3}/i);
|
|
if (!match) {
|
|
return [0, 0, 0];
|
|
}
|
|
let colorString = match[0];
|
|
if (match[0].length === 3) {
|
|
colorString = [...colorString].map((char) => char + char).join("");
|
|
}
|
|
const integer = Number.parseInt(colorString, 16);
|
|
const r = integer >> 16 & 255;
|
|
const g = integer >> 8 & 255;
|
|
const b = integer & 255;
|
|
return [r, g, b];
|
|
};
|
|
convert.rgb.hcg = function(rgb) {
|
|
const r = rgb[0] / 255;
|
|
const g = rgb[1] / 255;
|
|
const b = rgb[2] / 255;
|
|
const max = Math.max(Math.max(r, g), b);
|
|
const min = Math.min(Math.min(r, g), b);
|
|
const chroma = max - min;
|
|
let hue;
|
|
const grayscale = chroma < 1 ? min / (1 - chroma) : 0;
|
|
if (chroma <= 0) {
|
|
hue = 0;
|
|
} else if (max === r) {
|
|
hue = (g - b) / chroma % 6;
|
|
} else if (max === g) {
|
|
hue = 2 + (b - r) / chroma;
|
|
} else {
|
|
hue = 4 + (r - g) / chroma;
|
|
}
|
|
hue /= 6;
|
|
hue %= 1;
|
|
return [hue * 360, chroma * 100, grayscale * 100];
|
|
};
|
|
convert.hsl.hcg = function(hsl) {
|
|
const s = hsl[1] / 100;
|
|
const l = hsl[2] / 100;
|
|
const c = l < 0.5 ? 2 * s * l : 2 * s * (1 - l);
|
|
let f = 0;
|
|
if (c < 1) {
|
|
f = (l - 0.5 * c) / (1 - c);
|
|
}
|
|
return [hsl[0], c * 100, f * 100];
|
|
};
|
|
convert.hsv.hcg = function(hsv) {
|
|
const s = hsv[1] / 100;
|
|
const v = hsv[2] / 100;
|
|
const c = s * v;
|
|
let f = 0;
|
|
if (c < 1) {
|
|
f = (v - c) / (1 - c);
|
|
}
|
|
return [hsv[0], c * 100, f * 100];
|
|
};
|
|
convert.hcg.rgb = function(hcg) {
|
|
const h = hcg[0] / 360;
|
|
const c = hcg[1] / 100;
|
|
const g = hcg[2] / 100;
|
|
if (c === 0) {
|
|
return [g * 255, g * 255, g * 255];
|
|
}
|
|
const pure = [0, 0, 0];
|
|
const hi = h % 1 * 6;
|
|
const v = hi % 1;
|
|
const w = 1 - v;
|
|
let mg = 0;
|
|
switch (Math.floor(hi)) {
|
|
case 0: {
|
|
pure[0] = 1;
|
|
pure[1] = v;
|
|
pure[2] = 0;
|
|
break;
|
|
}
|
|
case 1: {
|
|
pure[0] = w;
|
|
pure[1] = 1;
|
|
pure[2] = 0;
|
|
break;
|
|
}
|
|
case 2: {
|
|
pure[0] = 0;
|
|
pure[1] = 1;
|
|
pure[2] = v;
|
|
break;
|
|
}
|
|
case 3: {
|
|
pure[0] = 0;
|
|
pure[1] = w;
|
|
pure[2] = 1;
|
|
break;
|
|
}
|
|
case 4: {
|
|
pure[0] = v;
|
|
pure[1] = 0;
|
|
pure[2] = 1;
|
|
break;
|
|
}
|
|
default: {
|
|
pure[0] = 1;
|
|
pure[1] = 0;
|
|
pure[2] = w;
|
|
}
|
|
}
|
|
mg = (1 - c) * g;
|
|
return [
|
|
(c * pure[0] + mg) * 255,
|
|
(c * pure[1] + mg) * 255,
|
|
(c * pure[2] + mg) * 255
|
|
];
|
|
};
|
|
convert.hcg.hsv = function(hcg) {
|
|
const c = hcg[1] / 100;
|
|
const g = hcg[2] / 100;
|
|
const v = c + g * (1 - c);
|
|
let f = 0;
|
|
if (v > 0) {
|
|
f = c / v;
|
|
}
|
|
return [hcg[0], f * 100, v * 100];
|
|
};
|
|
convert.hcg.hsl = function(hcg) {
|
|
const c = hcg[1] / 100;
|
|
const g = hcg[2] / 100;
|
|
const l = g * (1 - c) + 0.5 * c;
|
|
let s = 0;
|
|
if (l > 0 && l < 0.5) {
|
|
s = c / (2 * l);
|
|
} else if (l >= 0.5 && l < 1) {
|
|
s = c / (2 * (1 - l));
|
|
}
|
|
return [hcg[0], s * 100, l * 100];
|
|
};
|
|
convert.hcg.hwb = function(hcg) {
|
|
const c = hcg[1] / 100;
|
|
const g = hcg[2] / 100;
|
|
const v = c + g * (1 - c);
|
|
return [hcg[0], (v - c) * 100, (1 - v) * 100];
|
|
};
|
|
convert.hwb.hcg = function(hwb) {
|
|
const w = hwb[1] / 100;
|
|
const b = hwb[2] / 100;
|
|
const v = 1 - b;
|
|
const c = v - w;
|
|
let g = 0;
|
|
if (c < 1) {
|
|
g = (v - c) / (1 - c);
|
|
}
|
|
return [hwb[0], c * 100, g * 100];
|
|
};
|
|
convert.apple.rgb = function(apple) {
|
|
return [apple[0] / 65535 * 255, apple[1] / 65535 * 255, apple[2] / 65535 * 255];
|
|
};
|
|
convert.rgb.apple = function(rgb) {
|
|
return [rgb[0] / 255 * 65535, rgb[1] / 255 * 65535, rgb[2] / 255 * 65535];
|
|
};
|
|
convert.gray.rgb = function(args) {
|
|
return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];
|
|
};
|
|
convert.gray.hsl = function(args) {
|
|
return [0, 0, args[0]];
|
|
};
|
|
convert.gray.hsv = convert.gray.hsl;
|
|
convert.gray.hwb = function(gray) {
|
|
return [0, 100, gray[0]];
|
|
};
|
|
convert.gray.cmyk = function(gray) {
|
|
return [0, 0, 0, gray[0]];
|
|
};
|
|
convert.gray.lab = function(gray) {
|
|
return [gray[0], 0, 0];
|
|
};
|
|
convert.gray.hex = function(gray) {
|
|
const value = Math.round(gray[0] / 100 * 255) & 255;
|
|
const integer = (value << 16) + (value << 8) + value;
|
|
const string = integer.toString(16).toUpperCase();
|
|
return "000000".slice(string.length) + string;
|
|
};
|
|
convert.rgb.gray = function(rgb) {
|
|
const value = (rgb[0] + rgb[1] + rgb[2]) / 3;
|
|
return [value / 255 * 100];
|
|
};
|
|
|
|
// node_modules/color/node_modules/color-convert/route.js
|
|
function buildGraph() {
|
|
const graph = {};
|
|
const models2 = Object.keys(conversions_default);
|
|
for (let { length } = models2, i = 0; i < length; i++) {
|
|
graph[models2[i]] = {
|
|
// http://jsperf.com/1-vs-infinity
|
|
// micro-opt, but this is simple.
|
|
distance: -1,
|
|
parent: null
|
|
};
|
|
}
|
|
return graph;
|
|
}
|
|
function deriveBFS(fromModel) {
|
|
const graph = buildGraph();
|
|
const queue = [fromModel];
|
|
graph[fromModel].distance = 0;
|
|
while (queue.length > 0) {
|
|
const current = queue.pop();
|
|
const adjacents = Object.keys(conversions_default[current]);
|
|
for (let { length } = adjacents, i = 0; i < length; i++) {
|
|
const adjacent = adjacents[i];
|
|
const node = graph[adjacent];
|
|
if (node.distance === -1) {
|
|
node.distance = graph[current].distance + 1;
|
|
node.parent = current;
|
|
queue.unshift(adjacent);
|
|
}
|
|
}
|
|
}
|
|
return graph;
|
|
}
|
|
function link(from, to) {
|
|
return function(args) {
|
|
return to(from(args));
|
|
};
|
|
}
|
|
function wrapConversion(toModel, graph) {
|
|
const path = [graph[toModel].parent, toModel];
|
|
let fn = conversions_default[graph[toModel].parent][toModel];
|
|
let cur2 = graph[toModel].parent;
|
|
while (graph[cur2].parent) {
|
|
path.unshift(graph[cur2].parent);
|
|
fn = link(conversions_default[graph[cur2].parent][cur2], fn);
|
|
cur2 = graph[cur2].parent;
|
|
}
|
|
fn.conversion = path;
|
|
return fn;
|
|
}
|
|
function route(fromModel) {
|
|
const graph = deriveBFS(fromModel);
|
|
const conversion = {};
|
|
const models2 = Object.keys(graph);
|
|
for (let { length } = models2, i = 0; i < length; i++) {
|
|
const toModel = models2[i];
|
|
const node = graph[toModel];
|
|
if (node.parent === null) {
|
|
continue;
|
|
}
|
|
conversion[toModel] = wrapConversion(toModel, graph);
|
|
}
|
|
return conversion;
|
|
}
|
|
var route_default = route;
|
|
|
|
// node_modules/color/node_modules/color-convert/index.js
|
|
var convert2 = {};
|
|
var models = Object.keys(conversions_default);
|
|
function wrapRaw(fn) {
|
|
const wrappedFn = function(...args) {
|
|
const arg0 = args[0];
|
|
if (arg0 === void 0 || arg0 === null) {
|
|
return arg0;
|
|
}
|
|
if (arg0.length > 1) {
|
|
args = arg0;
|
|
}
|
|
return fn(args);
|
|
};
|
|
if ("conversion" in fn) {
|
|
wrappedFn.conversion = fn.conversion;
|
|
}
|
|
return wrappedFn;
|
|
}
|
|
function wrapRounded(fn) {
|
|
const wrappedFn = function(...args) {
|
|
const arg0 = args[0];
|
|
if (arg0 === void 0 || arg0 === null) {
|
|
return arg0;
|
|
}
|
|
if (arg0.length > 1) {
|
|
args = arg0;
|
|
}
|
|
const result = fn(args);
|
|
if (typeof result === "object") {
|
|
for (let { length } = result, i = 0; i < length; i++) {
|
|
result[i] = Math.round(result[i]);
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
if ("conversion" in fn) {
|
|
wrappedFn.conversion = fn.conversion;
|
|
}
|
|
return wrappedFn;
|
|
}
|
|
for (const fromModel of models) {
|
|
convert2[fromModel] = {};
|
|
Object.defineProperty(convert2[fromModel], "channels", { value: conversions_default[fromModel].channels });
|
|
Object.defineProperty(convert2[fromModel], "labels", { value: conversions_default[fromModel].labels });
|
|
const routes = route_default(fromModel);
|
|
const routeModels = Object.keys(routes);
|
|
for (const toModel of routeModels) {
|
|
const fn = routes[toModel];
|
|
convert2[fromModel][toModel] = wrapRounded(fn);
|
|
convert2[fromModel][toModel].raw = wrapRaw(fn);
|
|
}
|
|
}
|
|
var color_convert_default = convert2;
|
|
|
|
// node_modules/color/index.js
|
|
var skippedModels = [
|
|
// To be honest, I don't really feel like keyword belongs in color convert, but eh.
|
|
"keyword",
|
|
// Gray conflicts with some method names, and has its own method defined.
|
|
"gray",
|
|
// Shouldn't really be in color-convert either...
|
|
"hex"
|
|
];
|
|
var hashedModelKeys = {};
|
|
for (const model of Object.keys(color_convert_default)) {
|
|
hashedModelKeys[[...color_convert_default[model].labels].sort().join("")] = model;
|
|
}
|
|
var limiters = {};
|
|
function Color(object, model) {
|
|
if (!(this instanceof Color)) {
|
|
return new Color(object, model);
|
|
}
|
|
if (model && model in skippedModels) {
|
|
model = null;
|
|
}
|
|
if (model && !(model in color_convert_default)) {
|
|
throw new Error("Unknown model: " + model);
|
|
}
|
|
let i;
|
|
let channels;
|
|
if (object == null) {
|
|
this.model = "rgb";
|
|
this.color = [0, 0, 0];
|
|
this.valpha = 1;
|
|
} else if (object instanceof Color) {
|
|
this.model = object.model;
|
|
this.color = [...object.color];
|
|
this.valpha = object.valpha;
|
|
} else if (typeof object === "string") {
|
|
const result = color_string_default.get(object);
|
|
if (result === null) {
|
|
throw new Error("Unable to parse color from string: " + object);
|
|
}
|
|
this.model = result.model;
|
|
channels = color_convert_default[this.model].channels;
|
|
this.color = result.value.slice(0, channels);
|
|
this.valpha = typeof result.value[channels] === "number" ? result.value[channels] : 1;
|
|
} else if (object.length > 0) {
|
|
this.model = model || "rgb";
|
|
channels = color_convert_default[this.model].channels;
|
|
const newArray = Array.prototype.slice.call(object, 0, channels);
|
|
this.color = zeroArray(newArray, channels);
|
|
this.valpha = typeof object[channels] === "number" ? object[channels] : 1;
|
|
} else if (typeof object === "number") {
|
|
this.model = "rgb";
|
|
this.color = [
|
|
object >> 16 & 255,
|
|
object >> 8 & 255,
|
|
object & 255
|
|
];
|
|
this.valpha = 1;
|
|
} else {
|
|
this.valpha = 1;
|
|
const keys2 = Object.keys(object);
|
|
if ("alpha" in object) {
|
|
keys2.splice(keys2.indexOf("alpha"), 1);
|
|
this.valpha = typeof object.alpha === "number" ? object.alpha : 0;
|
|
}
|
|
const hashedKeys = keys2.sort().join("");
|
|
if (!(hashedKeys in hashedModelKeys)) {
|
|
throw new Error("Unable to parse color from object: " + JSON.stringify(object));
|
|
}
|
|
this.model = hashedModelKeys[hashedKeys];
|
|
const { labels } = color_convert_default[this.model];
|
|
const color = [];
|
|
for (i = 0; i < labels.length; i++) {
|
|
color.push(object[labels[i]]);
|
|
}
|
|
this.color = zeroArray(color);
|
|
}
|
|
if (limiters[this.model]) {
|
|
channels = color_convert_default[this.model].channels;
|
|
for (i = 0; i < channels; i++) {
|
|
const limit = limiters[this.model][i];
|
|
if (limit) {
|
|
this.color[i] = limit(this.color[i]);
|
|
}
|
|
}
|
|
}
|
|
this.valpha = Math.max(0, Math.min(1, this.valpha));
|
|
if (Object.freeze) {
|
|
Object.freeze(this);
|
|
}
|
|
}
|
|
Color.prototype = {
|
|
toString() {
|
|
return this.string();
|
|
},
|
|
toJSON() {
|
|
return this[this.model]();
|
|
},
|
|
string(places) {
|
|
let self = this.model in color_string_default.to ? this : this.rgb();
|
|
self = self.round(typeof places === "number" ? places : 1);
|
|
const arguments_ = self.valpha === 1 ? self.color : [...self.color, this.valpha];
|
|
return color_string_default.to[self.model](...arguments_);
|
|
},
|
|
percentString(places) {
|
|
const self = this.rgb().round(typeof places === "number" ? places : 1);
|
|
const arguments_ = self.valpha === 1 ? self.color : [...self.color, this.valpha];
|
|
return color_string_default.to.rgb.percent(...arguments_);
|
|
},
|
|
array() {
|
|
return this.valpha === 1 ? [...this.color] : [...this.color, this.valpha];
|
|
},
|
|
object() {
|
|
const result = {};
|
|
const { channels } = color_convert_default[this.model];
|
|
const { labels } = color_convert_default[this.model];
|
|
for (let i = 0; i < channels; i++) {
|
|
result[labels[i]] = this.color[i];
|
|
}
|
|
if (this.valpha !== 1) {
|
|
result.alpha = this.valpha;
|
|
}
|
|
return result;
|
|
},
|
|
unitArray() {
|
|
const rgb = this.rgb().color;
|
|
rgb[0] /= 255;
|
|
rgb[1] /= 255;
|
|
rgb[2] /= 255;
|
|
if (this.valpha !== 1) {
|
|
rgb.push(this.valpha);
|
|
}
|
|
return rgb;
|
|
},
|
|
unitObject() {
|
|
const rgb = this.rgb().object();
|
|
rgb.r /= 255;
|
|
rgb.g /= 255;
|
|
rgb.b /= 255;
|
|
if (this.valpha !== 1) {
|
|
rgb.alpha = this.valpha;
|
|
}
|
|
return rgb;
|
|
},
|
|
round(places) {
|
|
places = Math.max(places || 0, 0);
|
|
return new Color([...this.color.map(roundToPlace(places)), this.valpha], this.model);
|
|
},
|
|
alpha(value) {
|
|
if (value !== void 0) {
|
|
return new Color([...this.color, Math.max(0, Math.min(1, value))], this.model);
|
|
}
|
|
return this.valpha;
|
|
},
|
|
// Rgb
|
|
red: getset("rgb", 0, maxfn(255)),
|
|
green: getset("rgb", 1, maxfn(255)),
|
|
blue: getset("rgb", 2, maxfn(255)),
|
|
hue: getset(["hsl", "hsv", "hsl", "hwb", "hcg"], 0, (value) => (value % 360 + 360) % 360),
|
|
saturationl: getset("hsl", 1, maxfn(100)),
|
|
lightness: getset("hsl", 2, maxfn(100)),
|
|
saturationv: getset("hsv", 1, maxfn(100)),
|
|
value: getset("hsv", 2, maxfn(100)),
|
|
chroma: getset("hcg", 1, maxfn(100)),
|
|
gray: getset("hcg", 2, maxfn(100)),
|
|
white: getset("hwb", 1, maxfn(100)),
|
|
wblack: getset("hwb", 2, maxfn(100)),
|
|
cyan: getset("cmyk", 0, maxfn(100)),
|
|
magenta: getset("cmyk", 1, maxfn(100)),
|
|
yellow: getset("cmyk", 2, maxfn(100)),
|
|
black: getset("cmyk", 3, maxfn(100)),
|
|
x: getset("xyz", 0, maxfn(95.047)),
|
|
y: getset("xyz", 1, maxfn(100)),
|
|
z: getset("xyz", 2, maxfn(108.833)),
|
|
l: getset("lab", 0, maxfn(100)),
|
|
a: getset("lab", 1),
|
|
b: getset("lab", 2),
|
|
keyword(value) {
|
|
if (value !== void 0) {
|
|
return new Color(value);
|
|
}
|
|
return color_convert_default[this.model].keyword(this.color);
|
|
},
|
|
hex(value) {
|
|
if (value !== void 0) {
|
|
return new Color(value);
|
|
}
|
|
return color_string_default.to.hex(...this.rgb().round().color);
|
|
},
|
|
hexa(value) {
|
|
if (value !== void 0) {
|
|
return new Color(value);
|
|
}
|
|
const rgbArray = this.rgb().round().color;
|
|
let alphaHex = Math.round(this.valpha * 255).toString(16).toUpperCase();
|
|
if (alphaHex.length === 1) {
|
|
alphaHex = "0" + alphaHex;
|
|
}
|
|
return color_string_default.to.hex(...rgbArray) + alphaHex;
|
|
},
|
|
rgbNumber() {
|
|
const rgb = this.rgb().color;
|
|
return (rgb[0] & 255) << 16 | (rgb[1] & 255) << 8 | rgb[2] & 255;
|
|
},
|
|
luminosity() {
|
|
const rgb = this.rgb().color;
|
|
const lum = [];
|
|
for (const [i, element] of rgb.entries()) {
|
|
const chan = element / 255;
|
|
lum[i] = chan <= 0.04045 ? chan / 12.92 : ((chan + 0.055) / 1.055) ** 2.4;
|
|
}
|
|
return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
|
|
},
|
|
contrast(color2) {
|
|
const lum1 = this.luminosity();
|
|
const lum2 = color2.luminosity();
|
|
if (lum1 > lum2) {
|
|
return (lum1 + 0.05) / (lum2 + 0.05);
|
|
}
|
|
return (lum2 + 0.05) / (lum1 + 0.05);
|
|
},
|
|
level(color2) {
|
|
const contrastRatio = this.contrast(color2);
|
|
if (contrastRatio >= 7) {
|
|
return "AAA";
|
|
}
|
|
return contrastRatio >= 4.5 ? "AA" : "";
|
|
},
|
|
isDark() {
|
|
const rgb = this.rgb().color;
|
|
const yiq = (rgb[0] * 2126 + rgb[1] * 7152 + rgb[2] * 722) / 1e4;
|
|
return yiq < 128;
|
|
},
|
|
isLight() {
|
|
return !this.isDark();
|
|
},
|
|
negate() {
|
|
const rgb = this.rgb();
|
|
for (let i = 0; i < 3; i++) {
|
|
rgb.color[i] = 255 - rgb.color[i];
|
|
}
|
|
return rgb;
|
|
},
|
|
lighten(ratio) {
|
|
const hsl = this.hsl();
|
|
hsl.color[2] += hsl.color[2] * ratio;
|
|
return hsl;
|
|
},
|
|
darken(ratio) {
|
|
const hsl = this.hsl();
|
|
hsl.color[2] -= hsl.color[2] * ratio;
|
|
return hsl;
|
|
},
|
|
saturate(ratio) {
|
|
const hsl = this.hsl();
|
|
hsl.color[1] += hsl.color[1] * ratio;
|
|
return hsl;
|
|
},
|
|
desaturate(ratio) {
|
|
const hsl = this.hsl();
|
|
hsl.color[1] -= hsl.color[1] * ratio;
|
|
return hsl;
|
|
},
|
|
whiten(ratio) {
|
|
const hwb = this.hwb();
|
|
hwb.color[1] += hwb.color[1] * ratio;
|
|
return hwb;
|
|
},
|
|
blacken(ratio) {
|
|
const hwb = this.hwb();
|
|
hwb.color[2] += hwb.color[2] * ratio;
|
|
return hwb;
|
|
},
|
|
grayscale() {
|
|
const rgb = this.rgb().color;
|
|
const value = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
|
|
return Color.rgb(value, value, value);
|
|
},
|
|
fade(ratio) {
|
|
return this.alpha(this.valpha - this.valpha * ratio);
|
|
},
|
|
opaquer(ratio) {
|
|
return this.alpha(this.valpha + this.valpha * ratio);
|
|
},
|
|
rotate(degrees) {
|
|
const hsl = this.hsl();
|
|
let hue = hsl.color[0];
|
|
hue = (hue + degrees) % 360;
|
|
hue = hue < 0 ? 360 + hue : hue;
|
|
hsl.color[0] = hue;
|
|
return hsl;
|
|
},
|
|
mix(mixinColor, weight) {
|
|
if (!mixinColor || !mixinColor.rgb) {
|
|
throw new Error('Argument to "mix" was not a Color instance, but rather an instance of ' + typeof mixinColor);
|
|
}
|
|
const color1 = mixinColor.rgb();
|
|
const color2 = this.rgb();
|
|
const p = weight === void 0 ? 0.5 : weight;
|
|
const w = 2 * p - 1;
|
|
const a = color1.alpha() - color2.alpha();
|
|
const w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2;
|
|
const w2 = 1 - w1;
|
|
return Color.rgb(
|
|
w1 * color1.red() + w2 * color2.red(),
|
|
w1 * color1.green() + w2 * color2.green(),
|
|
w1 * color1.blue() + w2 * color2.blue(),
|
|
color1.alpha() * p + color2.alpha() * (1 - p)
|
|
);
|
|
}
|
|
};
|
|
for (const model of Object.keys(color_convert_default)) {
|
|
if (skippedModels.includes(model)) {
|
|
continue;
|
|
}
|
|
const { channels } = color_convert_default[model];
|
|
Color.prototype[model] = function(...arguments_) {
|
|
if (this.model === model) {
|
|
return new Color(this);
|
|
}
|
|
if (arguments_.length > 0) {
|
|
return new Color(arguments_, model);
|
|
}
|
|
return new Color([...assertArray(color_convert_default[this.model][model].raw(this.color)), this.valpha], model);
|
|
};
|
|
Color[model] = function(...arguments_) {
|
|
let color = arguments_[0];
|
|
if (typeof color === "number") {
|
|
color = zeroArray(arguments_, channels);
|
|
}
|
|
return new Color(color, model);
|
|
};
|
|
}
|
|
function roundTo(number, places) {
|
|
return Number(number.toFixed(places));
|
|
}
|
|
function roundToPlace(places) {
|
|
return function(number) {
|
|
return roundTo(number, places);
|
|
};
|
|
}
|
|
function getset(model, channel, modifier) {
|
|
model = Array.isArray(model) ? model : [model];
|
|
for (const m of model) {
|
|
(limiters[m] || (limiters[m] = []))[channel] = modifier;
|
|
}
|
|
model = model[0];
|
|
return function(value) {
|
|
let result;
|
|
if (value !== void 0) {
|
|
if (modifier) {
|
|
value = modifier(value);
|
|
}
|
|
result = this[model]();
|
|
result.color[channel] = value;
|
|
return result;
|
|
}
|
|
result = this[model]().color[channel];
|
|
if (modifier) {
|
|
result = modifier(result);
|
|
}
|
|
return result;
|
|
};
|
|
}
|
|
function maxfn(max) {
|
|
return function(v) {
|
|
return Math.max(0, Math.min(max, v));
|
|
};
|
|
}
|
|
function assertArray(value) {
|
|
return Array.isArray(value) ? value : [value];
|
|
}
|
|
function zeroArray(array, length) {
|
|
for (let i = 0; i < length; i++) {
|
|
if (typeof array[i] !== "number") {
|
|
array[i] = 0;
|
|
}
|
|
}
|
|
return array;
|
|
}
|
|
var color_default = Color;
|
|
|
|
// src/utils/colors.ts
|
|
var import_state6 = require("@codemirror/state");
|
|
var import_language8 = require("@codemirror/language");
|
|
|
|
// src/codemirror-extensions/inline-css.ts
|
|
var inlineCssLanguage = cssLanguage.configure({
|
|
top: "Styles"
|
|
});
|
|
|
|
// src/utils/colors.ts
|
|
function findColorValues(state) {
|
|
const matches = [];
|
|
const tree = (0, import_language8.syntaxTree)(state);
|
|
const doc = state.doc;
|
|
tree.cursor().iterate((nodeRef) => {
|
|
const { node } = nodeRef;
|
|
const nodeText = doc.sliceString(node.from, node.to);
|
|
if (isColorValue(node.name, nodeText)) {
|
|
matches.push({
|
|
from: node.from,
|
|
to: node.to,
|
|
color: nodeText
|
|
});
|
|
} else if (node.name === "Comment") {
|
|
const commentMatches = findColorsInComment(nodeText, node.from);
|
|
matches.push(...commentMatches);
|
|
}
|
|
});
|
|
return matches.sort((a, b) => a.from - b.from);
|
|
}
|
|
function convertToColorModel(color, model) {
|
|
switch (model) {
|
|
case "rgb":
|
|
return convertToRgb(color);
|
|
case "hsl":
|
|
return convertToHsl(color);
|
|
case "hex":
|
|
return convertToHex(color);
|
|
default:
|
|
return color;
|
|
}
|
|
}
|
|
function getColorModel(color) {
|
|
try {
|
|
if (color.startsWith("#")) return "hex";
|
|
if (isValidColorName(color)) return "hex";
|
|
return color_default(color).toJSON().model;
|
|
} catch (e) {
|
|
return "hex";
|
|
}
|
|
}
|
|
function convertToHex(color) {
|
|
try {
|
|
return color_default(color).hex().toLowerCase();
|
|
} catch (e) {
|
|
return color;
|
|
}
|
|
}
|
|
function convertToRgb(color) {
|
|
try {
|
|
return color_default(color).rgb().toString();
|
|
} catch (e) {
|
|
return color;
|
|
}
|
|
}
|
|
function convertToHsl(color) {
|
|
try {
|
|
return color_default(color).hsl().round().toString();
|
|
} catch (e) {
|
|
return color;
|
|
}
|
|
}
|
|
function isColorValue(nodeName, nodeText) {
|
|
switch (nodeName) {
|
|
case "ColorLiteral":
|
|
return true;
|
|
case "ValueName":
|
|
return isValidColorName(nodeText);
|
|
case "CallExpression":
|
|
return /^(rgb|rgba|hsl|hsla|hwb)\s*\(/.test(nodeText);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
var validColorNames = /* @__PURE__ */ new Set([
|
|
"aliceblue",
|
|
"antiquewhite",
|
|
"aqua",
|
|
"aquamarine",
|
|
"azure",
|
|
"beige",
|
|
"bisque",
|
|
"black",
|
|
"blanchedalmond",
|
|
"blue",
|
|
"blueviolet",
|
|
"brown",
|
|
"burlywood",
|
|
"cadetblue",
|
|
"chartreuse",
|
|
"chocolate",
|
|
"coral",
|
|
"cornflowerblue",
|
|
"cornsilk",
|
|
"crimson",
|
|
"cyan",
|
|
"darkblue",
|
|
"darkcyan",
|
|
"darkgoldenrod",
|
|
"darkgray",
|
|
"darkgreen",
|
|
"darkgrey",
|
|
"darkkhaki",
|
|
"darkmagenta",
|
|
"darkolivegreen",
|
|
"darkorange",
|
|
"darkorchid",
|
|
"darkred",
|
|
"darksalmon",
|
|
"darkseagreen",
|
|
"darkslateblue",
|
|
"darkslategray",
|
|
"darkslategrey",
|
|
"darkturquoise",
|
|
"darkviolet",
|
|
"deeppink",
|
|
"deepskyblue",
|
|
"dimgray",
|
|
"dimgrey",
|
|
"dodgerblue",
|
|
"firebrick",
|
|
"floralwhite",
|
|
"forestgreen",
|
|
"fuchsia",
|
|
"gainsboro",
|
|
"ghostwhite",
|
|
"gold",
|
|
"goldenrod",
|
|
"gray",
|
|
"green",
|
|
"greenyellow",
|
|
"grey",
|
|
"honeydew",
|
|
"hotpink",
|
|
"indianred",
|
|
"indigo",
|
|
"ivory",
|
|
"khaki",
|
|
"lavender",
|
|
"lavenderblush",
|
|
"lawngreen",
|
|
"lemonchiffon",
|
|
"lightblue",
|
|
"lightcoral",
|
|
"lightcyan",
|
|
"lightgoldenrodyellow",
|
|
"lightgray",
|
|
"lightgreen",
|
|
"lightgrey",
|
|
"lightpink",
|
|
"lightsalmon",
|
|
"lightseagreen",
|
|
"lightskyblue",
|
|
"lightslategray",
|
|
"lightslategrey",
|
|
"lightsteelblue",
|
|
"lightyellow",
|
|
"lime",
|
|
"limegreen",
|
|
"linen",
|
|
"magenta",
|
|
"maroon",
|
|
"mediumaquamarine",
|
|
"mediumblue",
|
|
"mediumorchid",
|
|
"mediumpurple",
|
|
"mediumseagreen",
|
|
"mediumslateblue",
|
|
"mediumspringgreen",
|
|
"mediumturquoise",
|
|
"mediumvioletred",
|
|
"midnightblue",
|
|
"mintcream",
|
|
"mistyrose",
|
|
"moccasin",
|
|
"navajowhite",
|
|
"navy",
|
|
"oldlace",
|
|
"olive",
|
|
"olivedrab",
|
|
"orange",
|
|
"orangered",
|
|
"orchid",
|
|
"palegoldenrod",
|
|
"palegreen",
|
|
"paleturquoise",
|
|
"palevioletred",
|
|
"papayawhip",
|
|
"peachpuff",
|
|
"peru",
|
|
"pink",
|
|
"plum",
|
|
"powderblue",
|
|
"purple",
|
|
"rebeccapurple",
|
|
"red",
|
|
"rosybrown",
|
|
"royalblue",
|
|
"saddlebrown",
|
|
"salmon",
|
|
"sandybrown",
|
|
"seagreen",
|
|
"seashell",
|
|
"sienna",
|
|
"silver",
|
|
"skyblue",
|
|
"slateblue",
|
|
"slategray",
|
|
"slategrey",
|
|
"snow",
|
|
"springgreen",
|
|
"steelblue",
|
|
"tan",
|
|
"teal",
|
|
"thistle",
|
|
"tomato",
|
|
"turquoise",
|
|
"violet",
|
|
"wheat",
|
|
"white",
|
|
"whitesmoke",
|
|
"yellow",
|
|
"yellowgreen"
|
|
]);
|
|
function isValidColorName(name) {
|
|
return validColorNames.has(name.toLowerCase());
|
|
}
|
|
function findColorsInComment(commentText, commentStart) {
|
|
var _a, _b;
|
|
const matches = [];
|
|
const withoutDelimiters = commentText.replace(/^\/\*|\*\/$/g, "");
|
|
const content = withoutDelimiters.trim();
|
|
if (!content) {
|
|
return matches;
|
|
}
|
|
const leadingWhitespaceMatch = withoutDelimiters.match(/^(\s*)/);
|
|
const leadingWhitespaceLength = leadingWhitespaceMatch ? (_b = (_a = leadingWhitespaceMatch[1]) == null ? void 0 : _a.length) != null ? _b : 0 : 0;
|
|
const totalOffset = 2 + leadingWhitespaceLength;
|
|
try {
|
|
const commentState = import_state6.EditorState.create({
|
|
doc: content,
|
|
extensions: [inlineCssLanguage]
|
|
});
|
|
const commentTree = (0, import_language8.syntaxTree)(commentState);
|
|
const commentDoc = commentState.doc;
|
|
commentTree.cursor().iterate((nodeRef) => {
|
|
const { node } = nodeRef;
|
|
const nodeText = commentDoc.sliceString(node.from, node.to);
|
|
if (isColorValue(node.name, nodeText)) {
|
|
const absoluteStart = commentStart + node.from + totalOffset;
|
|
const absoluteEnd = commentStart + node.to + totalOffset;
|
|
matches.push({
|
|
from: absoluteStart,
|
|
to: absoluteEnd,
|
|
color: nodeText
|
|
});
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.warn(
|
|
"Failed to parse comment as CSS, skipping color detection:",
|
|
error
|
|
);
|
|
}
|
|
return matches;
|
|
}
|
|
|
|
// src/codemirror-extensions/color-picker.ts
|
|
var ColorPickerWidget = class extends import_view7.WidgetType {
|
|
constructor(color, from, to, view) {
|
|
super();
|
|
this.color = color;
|
|
this.from = from;
|
|
this.to = to;
|
|
this.view = view;
|
|
}
|
|
eq(other) {
|
|
return this.color === other.color && this.from === other.from && this.to === other.to;
|
|
}
|
|
toDOM() {
|
|
const wrapper = document.createElement("span");
|
|
wrapper.className = "css-editor-color-picker-wrapper";
|
|
new import_obsidian4.ColorComponent(wrapper).setValue(convertToHex(this.color)).onChange((newColor) => {
|
|
const model = getColorModel(this.color);
|
|
this.view.dispatch({
|
|
changes: {
|
|
from: this.from,
|
|
to: this.to,
|
|
insert: convertToColorModel(newColor, model)
|
|
}
|
|
});
|
|
});
|
|
return wrapper;
|
|
}
|
|
};
|
|
var colorPickerPlugin = import_view7.ViewPlugin.fromClass(
|
|
class {
|
|
constructor(view) {
|
|
this.decorations = this.buildDecorations(view);
|
|
}
|
|
update(update) {
|
|
if (update.docChanged || update.viewportChanged) {
|
|
this.decorations = this.buildDecorations(update.view);
|
|
}
|
|
}
|
|
buildDecorations(view) {
|
|
const builder = new Array();
|
|
const doc = view.state.doc;
|
|
if (!doc || doc.length === 0) {
|
|
return import_view7.Decoration.set([]);
|
|
}
|
|
const colorMatches = findColorValues(view.state);
|
|
for (const match of colorMatches) {
|
|
if (match.from < 0 || match.to > doc.length || match.from >= match.to) {
|
|
continue;
|
|
}
|
|
const line = doc.lineAt(match.from);
|
|
if (view.viewport.from <= line.to && line.from <= view.viewport.to) {
|
|
const decoration = import_view7.Decoration.widget({
|
|
widget: new ColorPickerWidget(
|
|
match.color,
|
|
match.from,
|
|
match.to,
|
|
view
|
|
),
|
|
side: 1
|
|
});
|
|
builder.push(decoration.range(match.to));
|
|
}
|
|
}
|
|
return import_view7.Decoration.set(builder);
|
|
}
|
|
destroy() {
|
|
this.decorations = import_view7.Decoration.set([]);
|
|
}
|
|
},
|
|
{
|
|
decorations: (v) => v.decorations
|
|
}
|
|
);
|
|
|
|
// src/obsidian/workspace-helpers.ts
|
|
var import_obsidian5 = require("obsidian");
|
|
async function openView(workspace, type, openInNewTab, state) {
|
|
const leaf = workspace.getLeaf(openInNewTab);
|
|
await leaf.setViewState({
|
|
type,
|
|
state
|
|
});
|
|
workspace.setActiveLeaf(leaf);
|
|
}
|
|
async function detachCssFileLeaves(workspace, file) {
|
|
var _a;
|
|
const leaves = workspace.getLeavesOfType(VIEW_TYPE_CSS);
|
|
for (const leaf of leaves) {
|
|
if ((0, import_obsidian5.requireApiVersion)("1.7.2")) {
|
|
await leaf.loadIfDeferred();
|
|
}
|
|
if (((_a = leaf.getViewState().state) == null ? void 0 : _a.file) === file.name) {
|
|
leaf.detach();
|
|
}
|
|
}
|
|
}
|
|
|
|
// src/modals/CssSnippetDeleteConfirmModal.ts
|
|
var import_obsidian6 = require("obsidian");
|
|
var CssSnippetDeleteConfirmModal = class extends import_obsidian6.Modal {
|
|
constructor(app, plugin, file) {
|
|
super(app);
|
|
this.plugin = plugin;
|
|
this.file = file;
|
|
}
|
|
async onOpen() {
|
|
await super.onOpen();
|
|
this.titleEl.setText("Delete CSS snippet");
|
|
this.containerEl.addClass("css-editor-delete-confirm-modal");
|
|
this.buildForm();
|
|
}
|
|
buildForm() {
|
|
this.contentEl.createEl("p", {
|
|
text: `Are you sure you want to delete "${this.file.name}"?`
|
|
});
|
|
this.contentEl.createEl("p", {
|
|
text: "This action cannot be undone."
|
|
});
|
|
const buttonContainer = this.contentEl.createDiv(
|
|
"modal-button-container"
|
|
);
|
|
const dontAskAgainLabel = buttonContainer.createEl("label", {
|
|
cls: "mod-checkbox"
|
|
});
|
|
const dontAskAgainCheckbox = dontAskAgainLabel.createEl("input", {
|
|
type: "checkbox"
|
|
});
|
|
dontAskAgainCheckbox.insertAdjacentText("afterend", "Don't ask again");
|
|
new import_obsidian6.ButtonComponent(buttonContainer).setButtonText("Delete").setWarning().onClick(() => this.delete());
|
|
new import_obsidian6.ButtonComponent(buttonContainer).setButtonText("Cancel").onClick(() => this.close());
|
|
}
|
|
async delete() {
|
|
try {
|
|
const dontAskAgain = this.contentEl.querySelector(
|
|
'input[type="checkbox"]'
|
|
);
|
|
if (dontAskAgain == null ? void 0 : dontAskAgain.checked) {
|
|
this.plugin.settings.promptDelete = false;
|
|
await this.plugin.saveSettings();
|
|
}
|
|
await detachCssFileLeaves(this.app.workspace, this.file);
|
|
await deleteSnippetFile(this.app, this.file);
|
|
this.close();
|
|
} catch (err) {
|
|
handleError(err, "Failed to delete CSS file.");
|
|
}
|
|
}
|
|
};
|
|
|
|
// src/views/CssEditorView.ts
|
|
var VIEW_TYPE_CSS = "css-editor-view";
|
|
var CssEditorView = class extends import_obsidian7.ItemView {
|
|
constructor(leaf, plugin) {
|
|
var _a, _b;
|
|
super(leaf);
|
|
this.file = null;
|
|
this.isSavingTitle = false;
|
|
/** If the editor contents differ from the file contents on disk */
|
|
this.isEditorDirty = false;
|
|
this.requestSave = (0, import_obsidian7.debounce)(this.save.bind(this), 1e3);
|
|
this.plugin = plugin;
|
|
this.navigation = true;
|
|
this.editor = new import_view8.EditorView({
|
|
parent: this.contentEl,
|
|
extensions: [
|
|
basicExtensions,
|
|
lineWrap.of(
|
|
this.plugin.settings.lineWrap ? import_view8.EditorView.lineWrapping : []
|
|
),
|
|
indentSize.of(
|
|
import_language9.indentUnit.of("".padEnd(this.plugin.settings.indentSize))
|
|
),
|
|
historyCompartment.of((0, import_commands3.history)()),
|
|
colorPickerPlugin,
|
|
relativeLineNumberGutter.of(
|
|
(0, import_view8.lineNumbers)({
|
|
formatNumber: this.plugin.settings.relativeLineNumbers ? relativeLineNumbersFormatter : absoluteLineNumbers
|
|
})
|
|
),
|
|
((_b = (_a = this.app.vault).getConfig) == null ? void 0 : _b.call(_a, "vimMode")) ? vim() : [],
|
|
import_view8.EditorView.updateListener.of((update) => {
|
|
if (update.docChanged) {
|
|
this.isEditorDirty = true;
|
|
this.requestSave(update.state.doc.toString());
|
|
if (this.file) {
|
|
this.app.workspace.trigger(
|
|
"css-editor-change",
|
|
this.file,
|
|
update.state.doc.toString()
|
|
);
|
|
}
|
|
}
|
|
})
|
|
]
|
|
});
|
|
this.scope = new import_obsidian7.Scope(this.app.scope);
|
|
this.scope.register(null, "F2", () => {
|
|
if (!this.file) return;
|
|
if (this.titleEl.isShown()) {
|
|
focusAndSelectElement(this.titleEl);
|
|
} else {
|
|
new CssSnippetRenameModal(this.app, this.file).open();
|
|
}
|
|
});
|
|
}
|
|
getViewType() {
|
|
return VIEW_TYPE_CSS;
|
|
}
|
|
getIcon() {
|
|
return "file-code";
|
|
}
|
|
getDisplayText() {
|
|
var _a, _b;
|
|
return (_b = (_a = this.file) == null ? void 0 : _a.basename) != null ? _b : "No file open";
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
async onOpen() {
|
|
const timer = window.setInterval(() => {
|
|
this.editor.focus();
|
|
if (this.editor.hasFocus) clearInterval(timer);
|
|
}, 200);
|
|
this.registerInterval(timer);
|
|
if (import_obsidian7.Platform.isMobileApp) {
|
|
this.titleEl.addEventListener("touchstart", () => {
|
|
this.titleEl.contentEditable = "true";
|
|
});
|
|
} else {
|
|
this.titleEl.contentEditable = "true";
|
|
}
|
|
this.titleEl.addEventListener("focus", this.onTitleFocus.bind(this));
|
|
this.titleEl.addEventListener("blur", this.onTitleBlur.bind(this));
|
|
this.titleEl.addEventListener(
|
|
"keydown",
|
|
this.onTitleKeydown.bind(this)
|
|
);
|
|
this.registerEvent(
|
|
this.app.workspace.on("css-editor-change", (file, data) => {
|
|
var _a;
|
|
if (((_a = this.file) == null ? void 0 : _a.name) === file.name && this.getEditorData() !== data) {
|
|
this.dispatchEditorData(data);
|
|
}
|
|
})
|
|
);
|
|
this.registerEvent(
|
|
this.app.workspace.on("css-snippet-rename", (file, oldFileName) => {
|
|
var _a;
|
|
if (((_a = this.file) == null ? void 0 : _a.name) === oldFileName) {
|
|
this.file = file;
|
|
this.titleEl.setText(this.getDisplayText());
|
|
this.leaf.updateHeader();
|
|
this.app.workspace.requestSaveLayout();
|
|
}
|
|
})
|
|
);
|
|
this.registerEvent(
|
|
this.app.workspace.on("css-change", async (data) => {
|
|
if (!this.file) return;
|
|
if (typeof data === "object" && data !== null && "source" in data && (data == null ? void 0 : data.source) === "style-settings") {
|
|
return;
|
|
}
|
|
if (this.isEditorDirty) {
|
|
this.isEditorDirty = false;
|
|
return;
|
|
}
|
|
const contents = await readSnippetFile(this.app, this.file);
|
|
if (contents !== this.getEditorData()) {
|
|
this.dispatchEditorData(contents);
|
|
}
|
|
})
|
|
);
|
|
this.registerEvent(
|
|
this.app.workspace.on("leaf-menu", (menu, leaf) => {
|
|
if (leaf === this.leaf && !!this.file) {
|
|
menu.addItem((item) => {
|
|
item.setIcon("lucide-edit-3").setSection("action").setTitle("Rename...").onClick(() => {
|
|
if (this.file) {
|
|
new CssSnippetRenameModal(
|
|
this.app,
|
|
this.file
|
|
).open();
|
|
}
|
|
});
|
|
});
|
|
menu.addItem((item) => {
|
|
const isEnabled = this.isEnabled();
|
|
item.setIcon(
|
|
isEnabled ? "lucide-toggle-left" : "lucide-toggle-right"
|
|
).setSection("action").setTitle(
|
|
isEnabled ? "Disable snippet" : "Enable snippet"
|
|
).onClick(() => {
|
|
if (this.file) {
|
|
toggleSnippetFileState(this.app, this.file);
|
|
}
|
|
});
|
|
});
|
|
menu.addItem((item) => {
|
|
item.setIcon("lucide-trash-2").setSection("danger").setTitle("Delete snippet").setWarning(true).onClick(async () => {
|
|
if (this.file) {
|
|
if (this.plugin.settings.promptDelete) {
|
|
new CssSnippetDeleteConfirmModal(
|
|
this.app,
|
|
this.plugin,
|
|
this.file
|
|
).open();
|
|
} else {
|
|
try {
|
|
await detachCssFileLeaves(
|
|
this.app.workspace,
|
|
this.file
|
|
);
|
|
await deleteSnippetFile(
|
|
this.app,
|
|
this.file
|
|
);
|
|
} catch (err) {
|
|
handleError(
|
|
err,
|
|
"Failed to delete CSS file."
|
|
);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
})
|
|
);
|
|
}
|
|
onTitleFocus() {
|
|
var _a, _b;
|
|
this.titleEl.spellcheck = ((_b = (_a = this.app.vault).getConfig) == null ? void 0 : _b.call(_a, "spellcheck")) === true;
|
|
}
|
|
onTitleBlur() {
|
|
this.saveTitle(this.titleEl).catch(handleError);
|
|
this.titleEl.spellcheck = false;
|
|
if (import_obsidian7.Platform.isMobileApp) {
|
|
this.titleEl.contentEditable = "false";
|
|
}
|
|
this.editor.focus();
|
|
}
|
|
onTitleKeydown(event) {
|
|
if (!this.file) return;
|
|
if (event.isComposing) return;
|
|
if (event.key === "Escape") {
|
|
this.titleEl.setText(this.getDisplayText());
|
|
this.titleEl.blur();
|
|
}
|
|
if (event.key === "Enter" || event.key === "Tab") {
|
|
event.preventDefault();
|
|
this.saveTitle(this.titleEl).catch(handleError);
|
|
this.titleEl.blur();
|
|
}
|
|
}
|
|
async saveTitle(el) {
|
|
if (!this.file) return;
|
|
const newTitle = el.getText().trim();
|
|
if (newTitle === this.file.basename) return;
|
|
if (this.isSavingTitle) return;
|
|
this.isSavingTitle = true;
|
|
await renameSnippetFile(this.app, this.file, newTitle);
|
|
this.isSavingTitle = false;
|
|
}
|
|
getEditorData() {
|
|
return this.editor.state.doc.toString();
|
|
}
|
|
dispatchEditorTransaction(...specs) {
|
|
this.editor.dispatch(...specs);
|
|
}
|
|
dispatchEditorData(data) {
|
|
this.editor.dispatch({
|
|
changes: {
|
|
from: 0,
|
|
to: this.editor.state.doc.length,
|
|
insert: data
|
|
}
|
|
});
|
|
}
|
|
getState() {
|
|
var _a;
|
|
return {
|
|
file: (_a = this.file) == null ? void 0 : _a.name
|
|
};
|
|
}
|
|
async setState(state, result) {
|
|
var _a;
|
|
let file = null;
|
|
if (state && typeof state === "object") {
|
|
if ("filename" in state && typeof state.filename === "string") {
|
|
file = new CssFile(state.filename);
|
|
}
|
|
if ("file" in state) {
|
|
if (state.file instanceof CssFile) {
|
|
file = state.file;
|
|
} else if (typeof state.file === "string") {
|
|
file = new CssFile(state.file);
|
|
}
|
|
}
|
|
}
|
|
if (file) {
|
|
if (file.name !== ((_a = this.file) == null ? void 0 : _a.name)) {
|
|
try {
|
|
await this.loadFile(file);
|
|
} catch (e) {
|
|
await this.loadFile(null);
|
|
}
|
|
}
|
|
} else {
|
|
await this.loadFile(null);
|
|
}
|
|
result.history = true;
|
|
return super.setState({ file: file == null ? void 0 : file.name }, result);
|
|
}
|
|
async loadFile(file) {
|
|
this.file = file;
|
|
this.titleEl.setText(this.getDisplayText());
|
|
this.leaf.updateHeader();
|
|
const data = file ? await readSnippetFile(this.app, file) : "";
|
|
this.dispatchEditorData(data);
|
|
this.resetHistory();
|
|
this.app.workspace.requestSaveLayout();
|
|
}
|
|
resetHistory() {
|
|
this.editor.dispatch({
|
|
effects: [historyCompartment.reconfigure([])]
|
|
});
|
|
this.editor.dispatch({
|
|
effects: [historyCompartment.reconfigure((0, import_commands3.history)())]
|
|
});
|
|
}
|
|
isEnabled() {
|
|
var _a, _b;
|
|
if (!this.file) return false;
|
|
const currentState = (_b = (_a = this.app.customCss) == null ? void 0 : _a.enabledSnippets) == null ? void 0 : _b.has(
|
|
this.file.basename
|
|
);
|
|
return currentState || false;
|
|
}
|
|
/**
|
|
* You should almost always call `requestSave` instead of `save` to debounce the saving.
|
|
*/
|
|
async save(data) {
|
|
if (this.file) {
|
|
await writeSnippetFile(this.app, this.file, data);
|
|
}
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
async onClose() {
|
|
this.editor.destroy();
|
|
}
|
|
};
|
|
|
|
// src/modals/CssSnippetFuzzySuggestModal.ts
|
|
var import_obsidian8 = require("obsidian");
|
|
var CssSnippetFuzzySuggestModal = class extends import_obsidian8.FuzzySuggestModal {
|
|
constructor(app, plugin) {
|
|
super(app);
|
|
this.plugin = plugin;
|
|
this.scope.register(["Mod"], "Enter", (evt) => {
|
|
var _a, _b;
|
|
if (!evt.isComposing && ((_b = (_a = this.chooser) == null ? void 0 : _a.useSelectedItem) == null ? void 0 : _b.call(_a, evt))) {
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
this.scope.register(["Shift"], "Enter", (evt) => {
|
|
this.selectSuggestion(
|
|
{
|
|
item: new CssFile(this.inputEl.value),
|
|
match: { score: 0, matches: [] }
|
|
},
|
|
evt
|
|
);
|
|
return false;
|
|
});
|
|
this.scope.register(["Mod"], "Delete", (evt) => {
|
|
var _a, _b;
|
|
if (!evt.isComposing && ((_b = (_a = this.chooser) == null ? void 0 : _a.useSelectedItem) == null ? void 0 : _b.call(_a, evt))) {
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
this.scope.register([], "Tab", (evt) => {
|
|
var _a, _b;
|
|
if (this.chooser) {
|
|
const selectedItem = this.chooser.selectedItem;
|
|
const file = (_a = this.chooser.values[selectedItem]) == null ? void 0 : _a.item;
|
|
if (!file) return false;
|
|
const isEnabled = toggleSnippetFileState(this.app, file);
|
|
const buttonEl = (_b = this.chooser.suggestions[selectedItem]) == null ? void 0 : _b.querySelector(
|
|
".css-editor-status"
|
|
);
|
|
buttonEl == null ? void 0 : buttonEl.setText(isEnabled ? "enabled" : "disabled");
|
|
buttonEl == null ? void 0 : buttonEl.toggleClass("mod-cta", isEnabled);
|
|
}
|
|
return false;
|
|
});
|
|
this.containerEl.addClass("css-editor-quick-switcher-modal");
|
|
this.setPlaceholder("Find or create a CSS snippet...");
|
|
this.setInstructions([
|
|
{ command: "\u2191\u2193", purpose: "to navigate" },
|
|
{
|
|
command: import_obsidian8.Platform.isMacOS ? "\u2318 \u21B5" : "ctrl \u21B5",
|
|
purpose: "to open in new tab"
|
|
},
|
|
{ command: "shift \u21B5", purpose: "to create" },
|
|
{
|
|
command: import_obsidian8.Platform.isMacOS ? "\u2318 del" : "ctrl del",
|
|
purpose: "to delete"
|
|
},
|
|
{ command: "tab", purpose: "to enable/disable" },
|
|
{ command: "esc", purpose: "to dismiss" }
|
|
]);
|
|
}
|
|
isEnabled(item) {
|
|
var _a, _b;
|
|
const currentState = (_b = (_a = this.app.customCss) == null ? void 0 : _a.enabledSnippets) == null ? void 0 : _b.has(
|
|
item.basename
|
|
);
|
|
return currentState || false;
|
|
}
|
|
getItems() {
|
|
var _a;
|
|
if ((_a = this.app.customCss) == null ? void 0 : _a.snippets) {
|
|
return this.app.customCss.snippets.map((x) => new CssFile(x));
|
|
}
|
|
return [];
|
|
}
|
|
getItemText(item) {
|
|
return item.name;
|
|
}
|
|
renderSuggestion(item, el) {
|
|
super.renderSuggestion(item, el);
|
|
el.addClass("mod-complex");
|
|
if (el.hasChildNodes()) {
|
|
const existingChildren = Array.from(el.childNodes);
|
|
el.childNodes.forEach((child) => {
|
|
el.removeChild(child);
|
|
});
|
|
el.appendChild(
|
|
createDiv(
|
|
{ cls: "suggestion-content" },
|
|
(suggestionContentEl) => {
|
|
suggestionContentEl.appendChild(
|
|
createDiv(
|
|
{ cls: "css-editor-suggestion-name" },
|
|
(nestedEl) => {
|
|
existingChildren.forEach((child) => {
|
|
nestedEl.appendChild(child);
|
|
});
|
|
}
|
|
)
|
|
);
|
|
suggestionContentEl.appendChild(
|
|
createDiv(
|
|
{ cls: "css-editor-suggestion-description" },
|
|
(el2) => el2.appendText(
|
|
`${getSnippetDirectory(this.app)}${item.item.name}`
|
|
)
|
|
)
|
|
);
|
|
}
|
|
)
|
|
);
|
|
const isEnabled = this.isEnabled(item.item);
|
|
const isNewElement = this.inputEl.value.trim().length > 0 && item.match.score === 0;
|
|
if (!isNewElement) {
|
|
const button = new import_obsidian8.ButtonComponent(el).setButtonText(isEnabled ? "enabled" : "disabled").setClass("css-editor-status").onClick((e) => {
|
|
e.stopPropagation();
|
|
const newState = toggleSnippetFileState(
|
|
this.app,
|
|
item.item
|
|
);
|
|
button.setButtonText(newState ? "enabled" : "disabled");
|
|
if (newState) {
|
|
button.setCta();
|
|
} else {
|
|
button.removeCta();
|
|
}
|
|
});
|
|
if (isEnabled) {
|
|
button.setCta();
|
|
}
|
|
}
|
|
}
|
|
if (this.inputEl.value.trim().length > 0 && item.match.score === 0) {
|
|
el.appendChild(
|
|
createDiv({ cls: "suggestion-aux" }, (el2) => {
|
|
el2.appendChild(
|
|
createSpan({ cls: "suggestion-hotkey" }, (el3) => {
|
|
el3.appendText("Enter to create");
|
|
})
|
|
);
|
|
})
|
|
);
|
|
}
|
|
}
|
|
selectSuggestion(value, evt) {
|
|
try {
|
|
this.onChooseSuggestion(value, evt);
|
|
this.close();
|
|
} catch (err) {
|
|
handleError(err, "Failed to open CSS file.");
|
|
}
|
|
}
|
|
onChooseSuggestion(item, evt) {
|
|
const isCreateNewDueToNoSuggestion = this.inputEl.value.trim().length > 0 && item.match.score === 0;
|
|
if (isCreateNewDueToNoSuggestion && item.item) {
|
|
const openInNewTab = evt.metaKey;
|
|
this.plugin.createAndOpenSnippet(item.item.name, openInNewTab).catch((err) => {
|
|
handleError(err, "Failed to create and open CSS file.");
|
|
});
|
|
} else {
|
|
this.onChooseItem(item.item, evt);
|
|
}
|
|
}
|
|
onNoSuggestion() {
|
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
const item = this.inputEl.value.trim();
|
|
if (item.length > 0) {
|
|
(_b = (_a = this.chooser) == null ? void 0 : _a.setSuggestions) == null ? void 0 : _b.call(_a, [
|
|
{ item: new CssFile(item), match: { score: 0, matches: [] } }
|
|
]);
|
|
(_d = (_c = this.chooser) == null ? void 0 : _c.addMessage) == null ? void 0 : _d.call(
|
|
_c,
|
|
"No CSS snippets found. Enter to create a new one."
|
|
);
|
|
} else {
|
|
(_f = (_e = this.chooser) == null ? void 0 : _e.setSuggestions) == null ? void 0 : _f.call(_e, []);
|
|
(_h = (_g = this.chooser) == null ? void 0 : _g.addMessage) == null ? void 0 : _h.call(
|
|
_g,
|
|
"No CSS snippets found. Type to search..."
|
|
);
|
|
}
|
|
}
|
|
onChooseItem(item, evt) {
|
|
if (!item) return;
|
|
if (evt instanceof KeyboardEvent) {
|
|
if (evt.key === "Enter") {
|
|
const openInNewTab = evt.metaKey;
|
|
if (evt.shiftKey) {
|
|
this.plugin.createAndOpenSnippet(item.name, openInNewTab).catch((err) => {
|
|
handleError(
|
|
err,
|
|
"Failed to create and open CSS file."
|
|
);
|
|
});
|
|
} else {
|
|
openView(this.app.workspace, VIEW_TYPE_CSS, openInNewTab, {
|
|
file: item
|
|
}).catch(handleError);
|
|
}
|
|
} else if (evt.key === "Delete") {
|
|
if (this.plugin.settings.promptDelete) {
|
|
new CssSnippetDeleteConfirmModal(
|
|
this.app,
|
|
this.plugin,
|
|
item
|
|
).open();
|
|
} else {
|
|
Promise.all([
|
|
detachCssFileLeaves(this.app.workspace, item),
|
|
deleteSnippetFile(this.app, item)
|
|
]).then(() => {
|
|
new import_obsidian8.Notice(`${item.name} was deleted.`);
|
|
}).catch((err) => {
|
|
handleError(err, "Failed to delete CSS file.");
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
const openInNewTab = evt.metaKey;
|
|
openView(this.app.workspace, VIEW_TYPE_CSS, openInNewTab, {
|
|
file: item
|
|
}).catch(handleError);
|
|
}
|
|
}
|
|
};
|
|
|
|
// node_modules/monkey-around/mjs/index.js
|
|
function around(obj, factories) {
|
|
const removers = Object.keys(factories).map((key) => around1(obj, key, factories[key]));
|
|
return removers.length === 1 ? removers[0] : function() {
|
|
removers.forEach((r) => r());
|
|
};
|
|
}
|
|
function around1(obj, method, createWrapper) {
|
|
const original = obj[method], hadOwn = obj.hasOwnProperty(method);
|
|
let current = createWrapper(original);
|
|
if (original)
|
|
Object.setPrototypeOf(current, original);
|
|
Object.setPrototypeOf(wrapper, current);
|
|
obj[method] = wrapper;
|
|
return remove;
|
|
function wrapper(...args) {
|
|
if (current === original && obj[method] === wrapper)
|
|
remove();
|
|
return current.apply(this, args);
|
|
}
|
|
function remove() {
|
|
if (obj[method] === wrapper) {
|
|
if (hadOwn)
|
|
obj[method] = original;
|
|
else
|
|
delete obj[method];
|
|
}
|
|
if (current === original)
|
|
return;
|
|
current = original;
|
|
Object.setPrototypeOf(wrapper, original || Function);
|
|
}
|
|
}
|
|
|
|
// src/obsidian/ignore-obsidian-hotkey.ts
|
|
function ignoreObsidianHotkey(scope, keymapInfo, checkCallback) {
|
|
const uninstallCommand = around(scope, {
|
|
handleKey(originalMethod) {
|
|
return function(...args) {
|
|
const invokedHotkey = args[1];
|
|
if (isKeymapInfo(invokedHotkey) && keymapInfo.key === invokedHotkey.key && keymapInfo.modifiers === invokedHotkey.modifiers && checkCallback()) {
|
|
return true;
|
|
}
|
|
const result = originalMethod && originalMethod.apply(this, args);
|
|
return result;
|
|
};
|
|
}
|
|
});
|
|
return uninstallCommand;
|
|
}
|
|
function isKeymapInfo(hotkey) {
|
|
return !!hotkey && typeof hotkey === "object" && "key" in hotkey && typeof hotkey.key === "string" && "modifiers" in hotkey;
|
|
}
|
|
|
|
// src/modals/CssSnippetCreateModal.ts
|
|
var import_obsidian9 = require("obsidian");
|
|
var CssSnippetCreateModal = class extends import_obsidian9.Modal {
|
|
constructor(app, plugin) {
|
|
super(app);
|
|
this.value = "";
|
|
this.plugin = plugin;
|
|
}
|
|
async onOpen() {
|
|
await super.onOpen();
|
|
this.titleEl.setText("Create CSS snippet");
|
|
this.containerEl.addClass("css-editor-create-modal");
|
|
this.buildForm();
|
|
}
|
|
buildForm() {
|
|
const textInput = new import_obsidian9.TextComponent(this.contentEl);
|
|
textInput.setPlaceholder("CSS snippet file name (ex: snippet.css)");
|
|
textInput.onChange((val) => this.value = val);
|
|
textInput.inputEl.addEventListener("keydown", (evt) => {
|
|
this.handleKeydown(evt).catch(handleError);
|
|
});
|
|
const buttonContainer = this.contentEl.createDiv(
|
|
"modal-button-container"
|
|
);
|
|
new import_obsidian9.ButtonComponent(buttonContainer).setButtonText("Save").setCta().onClick(() => this.save());
|
|
new import_obsidian9.ButtonComponent(buttonContainer).setButtonText("Cancel").onClick(() => this.close());
|
|
}
|
|
async handleKeydown(evt) {
|
|
if (evt.key === "Escape") {
|
|
this.close();
|
|
} else if (evt.key === "Enter") {
|
|
await this.save(evt.metaKey);
|
|
}
|
|
}
|
|
async save(openInNewTab = false) {
|
|
try {
|
|
await this.plugin.createAndOpenSnippet(this.value, openInNewTab);
|
|
this.close();
|
|
} catch (err) {
|
|
handleError(err, "Failed to create and open CSS file.");
|
|
}
|
|
}
|
|
};
|
|
|
|
// src/obsidian/setting-tab.ts
|
|
var import_language10 = require("@codemirror/language");
|
|
var import_view9 = require("@codemirror/view");
|
|
var import_obsidian10 = require("obsidian");
|
|
function updateCSSEditorView(app, spec) {
|
|
app.workspace.getLeavesOfType(VIEW_TYPE_CSS).forEach((leaf) => {
|
|
if (leaf.view instanceof CssEditorView) {
|
|
leaf.view.dispatchEditorTransaction(spec);
|
|
}
|
|
});
|
|
}
|
|
var CSSEditorSettingTab = class extends import_obsidian10.PluginSettingTab {
|
|
constructor(app, plugin) {
|
|
super(app, plugin);
|
|
this.plugin = plugin;
|
|
}
|
|
display() {
|
|
this.containerEl.empty();
|
|
new import_obsidian10.Setting(this.containerEl).setName("Confirm CSS snippet deletion").setDesc("Prompt before CSS snippet deletion.").addToggle((toggle) => {
|
|
toggle.setValue(this.plugin.settings.promptDelete);
|
|
toggle.onChange(async (val) => {
|
|
this.plugin.settings.promptDelete = val;
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
new import_obsidian10.Setting(this.containerEl).setName("Line wrap").setDesc("Toggle line wrap in the editor.").addToggle((toggle) => {
|
|
toggle.setValue(this.plugin.settings.lineWrap);
|
|
toggle.onChange(async (val) => {
|
|
this.plugin.settings.lineWrap = val;
|
|
await this.plugin.saveSettings();
|
|
updateCSSEditorView(this.app, {
|
|
effects: lineWrap.reconfigure(
|
|
val ? import_view9.EditorView.lineWrapping : []
|
|
)
|
|
});
|
|
});
|
|
});
|
|
new import_obsidian10.Setting(this.containerEl).setName("Indent size").setDesc("Adjust the amount of spaces used for indentation.").addText((field) => {
|
|
field.setPlaceholder("2");
|
|
field.setValue(this.plugin.settings.indentSize.toString());
|
|
field.onChange(async (val) => {
|
|
val = val.replace(/\D/g, "");
|
|
field.setValue(val);
|
|
const size = parseInt(val);
|
|
this.plugin.settings.indentSize = size;
|
|
await this.plugin.saveSettings();
|
|
updateCSSEditorView(this.app, {
|
|
effects: indentSize.reconfigure(
|
|
import_language10.indentUnit.of("".padEnd(size))
|
|
)
|
|
});
|
|
});
|
|
});
|
|
new import_obsidian10.Setting(this.containerEl).setName("Relative line numbers").setDesc("Show line numbers relative to cursor position.").addToggle((toggle) => {
|
|
toggle.setValue(this.plugin.settings.relativeLineNumbers);
|
|
toggle.onChange(async (val) => {
|
|
this.plugin.settings.relativeLineNumbers = val;
|
|
await this.plugin.saveSettings();
|
|
updateCSSEditorView(this.app, {
|
|
effects: relativeLineNumberGutter.reconfigure(
|
|
(0, import_view9.lineNumbers)({
|
|
formatNumber: val ? relativeLineNumbersFormatter : absoluteLineNumbers
|
|
})
|
|
)
|
|
});
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
// src/main.ts
|
|
var DEFAULT_SETTINGS = {
|
|
promptDelete: true,
|
|
lineWrap: true,
|
|
indentSize: 2,
|
|
relativeLineNumbers: false
|
|
};
|
|
var CssEditorPlugin = class extends import_obsidian11.Plugin {
|
|
async onload() {
|
|
await this.loadSettings();
|
|
this.addCommand({
|
|
id: "create-css-snippet",
|
|
name: "Create CSS snippet",
|
|
callback: () => {
|
|
new CssSnippetCreateModal(this.app, this).open();
|
|
}
|
|
});
|
|
this.addCommand({
|
|
id: "open-quick-switcher",
|
|
name: "Open quick switcher",
|
|
callback: () => {
|
|
new CssSnippetFuzzySuggestModal(this.app, this).open();
|
|
}
|
|
});
|
|
this.addCommand({
|
|
id: "delete-css-snippet",
|
|
name: "Delete CSS snippet",
|
|
checkCallback: (checking) => {
|
|
const activeCssEditorView = this.app.workspace.getActiveViewOfType(CssEditorView);
|
|
if (!activeCssEditorView) return false;
|
|
const { file } = activeCssEditorView.getState();
|
|
if (!file) return false;
|
|
if (checking) return true;
|
|
const cssFile = new CssFile(file);
|
|
if (this.settings.promptDelete) {
|
|
new CssSnippetDeleteConfirmModal(
|
|
this.app,
|
|
this,
|
|
cssFile
|
|
).open();
|
|
} else {
|
|
detachCssFileLeaves(this.app.workspace, cssFile).then(async () => {
|
|
await deleteSnippetFile(this.app, cssFile);
|
|
new import_obsidian11.Notice(`"${cssFile.name}" was deleted.`);
|
|
}).catch((err) => {
|
|
handleError(err, "Failed to delete CSS file.");
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
});
|
|
this.addCommand({
|
|
id: "toggle-css-snippet-enabled-status",
|
|
name: "Toggle the enabled/disabled state of CSS snippet",
|
|
checkCallback: (checking) => {
|
|
const activeCssEditorView = this.app.workspace.getActiveViewOfType(CssEditorView);
|
|
if (!activeCssEditorView) return false;
|
|
const { file } = activeCssEditorView.getState();
|
|
if (!file) return false;
|
|
if (checking) return true;
|
|
const cssFile = new CssFile(file);
|
|
const isEnabled = toggleSnippetFileState(this.app, cssFile);
|
|
new import_obsidian11.Notice(
|
|
`"${cssFile.name}" is now ${isEnabled ? "enabled" : "disabled"}.`
|
|
);
|
|
return true;
|
|
}
|
|
});
|
|
this.register(
|
|
ignoreObsidianHotkey(
|
|
this.app.scope,
|
|
{ key: "/", modifiers: "Meta" },
|
|
() => !!this.app.workspace.getActiveViewOfType(CssEditorView)
|
|
)
|
|
);
|
|
this.registerView(
|
|
VIEW_TYPE_CSS,
|
|
(leaf) => new CssEditorView(leaf, this)
|
|
);
|
|
this.settingTab = new CSSEditorSettingTab(this.app, this);
|
|
this.addSettingTab(this.settingTab);
|
|
}
|
|
onunload() {
|
|
}
|
|
async loadSettings() {
|
|
this.settings = Object.assign(
|
|
{},
|
|
DEFAULT_SETTINGS,
|
|
await this.loadData()
|
|
);
|
|
}
|
|
async saveSettings() {
|
|
await this.saveData(this.settings);
|
|
}
|
|
async createAndOpenSnippet(filename, openInNewTab) {
|
|
var _a, _b;
|
|
const file = await createSnippetFile(this.app, filename, "");
|
|
(_b = (_a = this.app.customCss) == null ? void 0 : _a.setCssEnabledStatus) == null ? void 0 : _b.call(_a, file.basename, true);
|
|
new import_obsidian11.Notice(`${file.name} was created.`);
|
|
await openView(this.app.workspace, VIEW_TYPE_CSS, openInNewTab, {
|
|
file
|
|
});
|
|
}
|
|
};
|
|
|
|
/* nosourcemap */ |