From 40b5caba321ed84fd73db53b6eb1be0aa5088654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Str=C3=B6mberg?= Date: Thu, 6 Dec 2018 20:37:30 +0100 Subject: [PATCH] Calva-lib -> git --- .gitignore | 12 + package-lock.json | 828 +++++++++++++++++++++++++++++ package.json | 31 ++ packages/.gitignore | 2 + packages/package.json | 17 + shadow-cljs.edn | 21 + src/calva/fmt/editor.cljs | 37 ++ src/calva/fmt/formatter.cljs | 305 +++++++++++ src/calva/fmt/inferer.cljs | 36 ++ src/calva/fmt/playground.cljs | 145 +++++ src/calva/fmt/util.cljs | 56 ++ src/calva/js_utils.cljs | 7 + src/calva/main.cljs | 6 + test/calva/fmt/editor_test.cljs | 13 + test/calva/fmt/formatter_test.cljs | 238 +++++++++ test/calva/fmt/util_test.cljs | 60 +++ 16 files changed, 1814 insertions(+) create mode 100644 .gitignore create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 packages/.gitignore create mode 100644 packages/package.json create mode 100644 shadow-cljs.edn create mode 100644 src/calva/fmt/editor.cljs create mode 100644 src/calva/fmt/formatter.cljs create mode 100644 src/calva/fmt/inferer.cljs create mode 100644 src/calva/fmt/playground.cljs create mode 100644 src/calva/fmt/util.cljs create mode 100644 src/calva/js_utils.cljs create mode 100644 src/calva/main.cljs create mode 100644 test/calva/fmt/editor_test.cljs create mode 100644 test/calva/fmt/formatter_test.cljs create mode 100644 test/calva/fmt/util_test.cljs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ecc016 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +node_modules +.vscode-test/ +*.vsix +.shadow-cljs/ +.nrepl-port +pom.xml +*.idea/ +*.DS_Store +out/ +lib/ +test-out/ +.vscode/settings.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d2bc704 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,828 @@ +{ + "name": "calva-lib", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ace.improved": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ace.improved/-/ace.improved-0.2.1.tgz", + "integrity": "sha1-TXRij8QxsJzcqh+ysj0eyDxdLzI=" + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "inherits": "2.0.1", + "minimalistic-assert": "1.0.1" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "util": { + "version": "0.10.3", + "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "1.0.3", + "cipher-base": "1.0.4", + "create-hash": "1.2.0", + "evp_bytestokey": "1.0.3", + "inherits": "2.0.1", + "safe-buffer": "5.1.2" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "1.2.0", + "browserify-des": "1.0.2", + "evp_bytestokey": "1.0.3" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "1.0.4", + "des.js": "1.0.0", + "inherits": "2.0.1", + "safe-buffer": "5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "randombytes": "2.0.6" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "elliptic": "6.4.1", + "inherits": "2.0.1", + "parse-asn1": "5.1.1" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "1.0.7" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "1.3.0", + "ieee754": "1.1.12", + "isarray": "1.0.0" + } + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "2.0.1", + "safe-buffer": "5.1.2" + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "elliptic": "6.4.1" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "1.0.4", + "inherits": "2.0.1", + "md5.js": "1.3.5", + "ripemd160": "2.0.2", + "sha.js": "2.4.11" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "1.0.4", + "create-hash": "1.2.0", + "inherits": "2.0.1", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.2", + "sha.js": "2.4.11" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "1.0.1", + "browserify-sign": "4.0.4", + "create-ecdh": "4.0.3", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "diffie-hellman": "5.0.3", + "inherits": "2.0.1", + "pbkdf2": "3.0.17", + "public-encrypt": "4.0.3", + "randombytes": "2.0.6", + "randomfill": "1.0.4" + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "2.0.1", + "minimalistic-assert": "1.0.1" + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "miller-rabin": "4.0.1", + "randombytes": "2.0.6" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0", + "hash.js": "1.1.7", + "hmac-drbg": "1.0.1", + "inherits": "2.0.1", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" + } + }, + "events": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "1.3.5", + "safe-buffer": "5.1.2" + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "2.0.1", + "safe-buffer": "5.1.2" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "1.1.7", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "3.0.4", + "inherits": "2.0.1", + "safe-buffer": "5.1.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimist": { + "version": "0.0.8", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "node-libs-browser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", + "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", + "dev": true, + "requires": { + "assert": "1.4.1", + "browserify-zlib": "0.2.0", + "buffer": "4.9.1", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.12.0", + "domain-browser": "1.2.0", + "events": "1.1.1", + "https-browserify": "1.0.0", + "os-browserify": "0.3.0", + "path-browserify": "0.0.0", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "readable-stream": "2.3.6", + "stream-browserify": "2.0.1", + "stream-http": "2.8.3", + "string_decoder": "1.2.0", + "timers-browserify": "2.0.10", + "tty-browserify": "0.0.0", + "url": "0.11.0", + "util": "0.10.4", + "vm-browserify": "0.0.4" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "pako": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.7.tgz", + "integrity": "sha512-3HNK5tW4x8o5mO8RuHZp3Ydw9icZXx0RANAOMzlMzx7LVXhMJ4mo3MOBpzyd7r/+RUu8BmndP47LXT+vzjtWcQ==", + "dev": true + }, + "paredit.js": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/paredit.js/-/paredit.js-0.3.4.tgz", + "integrity": "sha512-b6t7ORo/MwT6xvRiuu1c1do3+CAUd7/0rgc1d3qNHUeP64zxy4ttLIvK7SEHzyfyDLvD9pPuV9mYKHf6MgUkmg==", + "requires": { + "ace.improved": "0.2.1" + } + }, + "parinfer": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/parinfer/-/parinfer-3.12.0.tgz", + "integrity": "sha512-iViQ8vtJ6CEa9x0SxcQCtsOYSCVVu7ILnuUJfKHPEGjxM0Z6R1EdAJX3kWyqZDrszvQyYWl/XpD9pN2IFgSF/g==" + }, + "parse-asn1": { + "version": "5.1.1", + "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", + "dev": true, + "requires": { + "asn1.js": "4.10.1", + "browserify-aes": "1.2.0", + "create-hash": "1.2.0", + "evp_bytestokey": "1.0.3", + "pbkdf2": "3.0.17" + } + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.2", + "sha.js": "2.4.11" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.2.0", + "parse-asn1": "5.1.1", + "randombytes": "2.0.6", + "safe-buffer": "5.1.2" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "2.0.6", + "safe-buffer": "5.1.2" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + } + } + }, + "readline-sync": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.9.tgz", + "integrity": "sha1-PtqOZfI80qF+YTAbHwADOWr17No=", + "dev": true + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "3.0.4", + "inherits": "2.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "2.0.1", + "safe-buffer": "5.1.2" + } + }, + "shadow-cljs": { + "version": "2.7.7", + "resolved": "https://registry.npmjs.org/shadow-cljs/-/shadow-cljs-2.7.7.tgz", + "integrity": "sha512-xxibRH70ciIYPrSWWpxGkULDH6dgULLUYMKYQXnijP9HAmQQkCLpc/Q2z14yfgLUFKGnYf4e64GvOI1ZcFVzpQ==", + "dev": true, + "requires": { + "mkdirp": "0.5.1", + "node-libs-browser": "2.1.0", + "readline-sync": "1.4.9", + "shadow-cljs-jar": "1.3.0", + "source-map-support": "0.4.18", + "which": "1.3.1", + "ws": "3.3.3" + } + }, + "shadow-cljs-jar": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/shadow-cljs-jar/-/shadow-cljs-jar-1.3.0.tgz", + "integrity": "sha512-KReNVgFVM2ZPPGCP8rsCPqtlee/+SwXyoeEqbAXBO7jlpoNnNee2x4fiRg/Pr/vXGEkV/Ez5l4qdNSU1Na+1Jg==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "0.5.7" + } + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true, + "requires": { + "inherits": "2.0.1", + "readable-stream": "2.3.6" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "3.0.0", + "inherits": "2.0.1", + "readable-stream": "2.3.6", + "to-arraybuffer": "1.0.1", + "xtend": "4.0.1" + } + }, + "string_decoder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", + "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "dev": true, + "requires": { + "setimmediate": "1.0.5" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "1.0.0", + "safe-buffer": "5.1.2", + "ultron": "1.1.1" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..0b2a611 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "calva-lib", + "displayName": "The Calva Library", + "description": "A common library for the Calva Family of extensions", + "version": "0.0.1", + "keywords": [ + "Clojure", + "ClojureScript", + "EDN", + "Formatting", + "Pretty" + ], + "repository": { + "type": "git", + "url": "https://github.com/BetterThanTomorrow/calva-lib.git" + }, + "scripts": { + "watch": "npx shadow-cljs watch :test :calva-lib", + "release": "npx shadow-cljs release :calva-lib", + "compile": "npx shadow-cljs compile :calva-lib", + "test": "node ./node_modules/vscode/bin/test" + }, + "devDependencies": { + "shadow-cljs": "^2.7.6" + }, + "dependencies": { + "paredit.js": "^0.3.4", + "parinfer": "^3.12.0" + }, + "license": "MIT" +} diff --git a/packages/.gitignore b/packages/.gitignore new file mode 100644 index 0000000..aa4a6da --- /dev/null +++ b/packages/.gitignore @@ -0,0 +1,2 @@ +*.js +*.js.map \ No newline at end of file diff --git a/packages/package.json b/packages/package.json new file mode 100644 index 0000000..f6a361f --- /dev/null +++ b/packages/package.json @@ -0,0 +1,17 @@ +{ + "name": "@pez/calva-lib", + "version": "0.0.1", + "description": "The Calva Common Library", + "main": "calva.fmt.formatter.js", + "keywords": [ + "clojurescript", + "cljs", + "shadow-cljs" + ], + "author": "Peter Strömberg", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/BetterThanTomorrow/calva-lib.git" + } +} \ No newline at end of file diff --git a/shadow-cljs.edn b/shadow-cljs.edn new file mode 100644 index 0000000..4a0bf71 --- /dev/null +++ b/shadow-cljs.edn @@ -0,0 +1,21 @@ +{:dependencies [[pez/cljfmt "0.6.2-ALIGN"] + #_[zprint "0.4.11"] + [cider/cider-nrepl "0.19.0-SNAPSHOT"]] + + :source-paths ["src" "test"] + + :builds {:calva-lib + {:target :npm-module + :runtime :node + :entries [calva.fmt.formatter + calva.fmt.inferer + calva.js-utils + calva.main] + :output-dir "packages/calva_lib" + :devtools {:before-load-async calva.main/stop + :after-load calva.main/start}} + :test + {:target :node-test + :output-to "test-out/tests.js" + :ns-regexp "-test$" + :autorun true}}} \ No newline at end of file diff --git a/src/calva/fmt/editor.cljs b/src/calva/fmt/editor.cljs new file mode 100644 index 0000000..2342c4f --- /dev/null +++ b/src/calva/fmt/editor.cljs @@ -0,0 +1,37 @@ +(ns calva.fmt.editor + (:require [calva.fmt.util :as util])) + + +(defn raplacement-edits-for-diffing-lines + "Returns a list of replacement edits to apply to `old-text` to get `new-text`. + Edits will be in the form `[:replace [range] text]`, + where `range` is in the form `[[start-line start-char] [end-line end-char]]`. + NB: The two versions need to have the same amount of lines." + [old-text new-text] + (let [old-lines (util/split-into-lines old-text) + new-lines (util/split-into-lines new-text)] + (->> (map vector (range) old-lines new-lines) + (remove (fn [[line o n]] (= o n))) + (mapv (fn [[line o n]] + {:edit "replace" + :start {:line line + :character 0} + :end {:line line + :character (count o)} + :text n}))))) + + +(comment + (raplacement-edits-for-diffing-lines "foo\nfooo\nbar\nbar" + "foo\nbar\nbaz\nbar") + (->> (map vector + [:foo :foo :foo] + [:foo :bar :foo] + (range)) + (remove (fn [[o n i]] + (= o n)))) + (filter some? + (map (fn [o n line] (when-not (= o n) [o n line])) + [:foo :foo :foo] + [:foo :bar :foo] + (range)))) \ No newline at end of file diff --git a/src/calva/fmt/formatter.cljs b/src/calva/fmt/formatter.cljs new file mode 100644 index 0000000..085c1e9 --- /dev/null +++ b/src/calva/fmt/formatter.cljs @@ -0,0 +1,305 @@ +(ns calva.fmt.formatter + (:require [cljfmt.core :as cljfmt] + #_[zprint.core :refer [zprint-str]] + ["paredit.js" :as paredit] + ["parinfer" :as parinfer] + [calva.js-utils :refer [cljify jsify]] + [calva.fmt.util :as util] + [calva.fmt.editor :as editor])) + + +(defn format-text + [{:keys [range-text config] :as m}] + (try + #_(assoc m :range-text (zprint-str range-text {:parse-string-all? true + :style :community + :fn-force-nl #{:arg1-body}})) + (assoc m :range-text (cljfmt/reformat-string range-text config)) + (catch js/Error e + (assoc m :error (.-message e))))) + + +(defn current-line-empty? + "Figure out if `:current-line` is empty" + [{:keys [current-line]}] + (some? (re-find #"^\s*$" current-line))) + + +(defn indent-before-range + "Figures out how much extra indentation to add based on the length of the line before the range" + [{:keys [all-text range]}] + (let [start (first range) + end (last range)] + (if (= start end) + 0 + (-> (subs all-text 0 (first range)) + (util/split-into-lines) + (last) + (count))))) + + +(defn enclosing-range + "Expands the range from `idx` up to any enclosing list/vector/map/string" + [{:keys [all-text idx] :as m}] + (assoc m :range + (let [ast (paredit/parse all-text) + range ((.. paredit -navigator -sexpRange) ast idx) + enclosing (try + (if (some? range) + (loop [range range] + (let [text (apply subs all-text range)] + (if (and (some? range) + (or (= idx (first range)) + (= idx (last range)) + (not (util/enclosing? text)))) + (let [expanded-range ((.. paredit -navigator -sexpRangeExpansion) ast (first range) (last range))] + (if (and (some? expanded-range) (not= expanded-range range)) + (recur expanded-range) + (cljify range))) + (cljify range)))) + [idx idx]) + (catch js/Error e + ((.. paredit -navigator -rangeForDefun) ast idx)))] + (loop [enclosing enclosing] + (let [expanded-range ((.. paredit -navigator -sexpRangeExpansion) ast (first enclosing) (last enclosing))] + (if (some? expanded-range) + (let [text (apply subs all-text expanded-range)] + (if (and (not= expanded-range enclosing) (re-find #"^['`#?_]" text)) + (recur expanded-range) + (cljify enclosing))) + (cljify enclosing))))))) + + +(comment + (enclosing-range {:all-text " ([]\n[])" + :idx 6}) + (enclosing-range {:all-text " (foo)" + :idx 4}) + (enclosing-range {:all-text " \"(foo)\"" + :idx 4}) + (enclosing-range {:all-text " ((foo))" + :idx 4}) + (enclosing-range {:all-text " (#{foo})" + :idx 5}) + (def s "(foo \"([\\[\\]\\])\")") + (count s) + (enclosing-range {:all-text s + :idx 4}) + (pr-str s) + (def s " (\"([\\[\\]\\])\")") + (cljfmt/reformat-string (pr-str s) + {:remove-surrounding-whitespace? false + :remove-trailing-whitespace? false + :remove-consecutive-blank-lines? false + :align-associative? true})) + +(defn add-head-and-tail + "Splits `:all-text` at `:idx` in `:head` and `:tail`" + [{:keys [all-text idx] :as m}] + (-> m + (assoc :head (subs all-text 0 idx) + :tail (subs all-text idx)))) + + +(defn add-current-line + "Finds the text of the current line in `text` from cursor position `index`" + [{:keys [head tail] :as m}] + (-> m + (assoc :current-line + (str (second (re-find #"\n?(.*)$" head)) + (second (re-find #"^(.*)\n?" tail)))))) + + +(defn- normalize-indents + "Normalizes indents based on where the text starts on the first line" + [{:keys [range-text] :as m}] + (let [indent-before (apply str (repeat (indent-before-range m) " ")) + lines (clojure.string/split range-text #"\r?\n(?!\s*;)" -1)] + (assoc m :range-text (clojure.string/join (str "\n" indent-before) lines)))) + + +(defn index-for-tail-in-range + "Find index for the `tail` in `text` disregarding whitespace" + [{:keys [range-text range-tail on-type idx] :as m}] + #_(if-not (= range-text "0") + (assoc m :new-index idx)) + (let [leading-space-length (count (re-find #"^[ \t]*" range-tail)) + tail-pattern (-> range-tail + (util/escape-regexp) + (clojure.string/replace #"^[ \t]+" "") + (clojure.string/replace #"\s+" "\\s*")) + tail-pattern (if (and on-type (re-find #"^\n" range-tail)) + (str "\n+" tail-pattern) + tail-pattern) + pos (util/re-pos-first (str " {0," leading-space-length "}" tail-pattern "$") range-text)] + (assoc m :new-index pos))) + + +(defn format-text-at-range + "Formats text from all-text at the range" + [{:keys [all-text range idx config on-type] :as m}] + (let [indent-before (indent-before-range m) + padding (apply str (repeat indent-before " ")) + range-text (subs all-text (first range) (last range)) + padded-text (str padding range-text) + range-index (- idx (first range)) + tail (subs range-text range-index) + formatted-m (format-text (assoc m :range-text padded-text))] + (-> (assoc m :range-text (subs (:range-text formatted-m) indent-before)) + (assoc :range-tail tail) + (index-for-tail-in-range)))) + +(comment + (format-text-at-range {:all-text " '([]\n[])", + :idx 7, + :on-type true, + :head " '([]\n", + :tail "[])", + :current-line "[])", + :range [4 9]})) + +(defn format-text-at-range-old + "Formats text from all-text at the range" + [{:keys [all-text range idx config on-type] :as m}] + (let [range-text (subs all-text (first range) (last range)) + range-index (- idx (first range)) + tail (subs range-text range-index) + formatted-m (format-text (assoc m :range-text range-text)) + normalized-m (normalize-indents formatted-m)] + (-> normalized-m + (assoc :range-tail tail) + (index-for-tail-in-range)))) + + +(defn add-indent-token-if-empty-current-line + "If `:current-line` is empty add an indent token at `:idx`" + [{:keys [head tail] :as m}] + (let [indent-token "0"] + (if (current-line-empty? m) + (assoc m :all-text (str head indent-token tail)) + m))) + + +(defn remove-indent-token-if-empty-current-line + "If an indent token was added, lets remove it. Not forgetting to shrink `:range`" + [{:keys [range-text range new-index] :as m}] + (if (current-line-empty? m) + (assoc m :range-text (str (subs range-text 0 new-index) (subs range-text (inc new-index))) + :range [(first range) (dec (second range))]) + m)) + + +(defn ^:export format-text-at-idx + "Formats the enclosing range of text surrounding idx" + [{:keys [all-text idx] :as m}] + (-> m + (add-head-and-tail) + (add-current-line) + (add-indent-token-if-empty-current-line) + (enclosing-range) + (format-text-at-range) + (remove-indent-token-if-empty-current-line))) + + +(defn ^:export format-text-at-idx-on-type + "Relax formating some when used as an on-type handler" + [m] + (-> m + (assoc :on-type true) + (assoc-in [:config :remove-surrounding-whitespace?] false) + (assoc-in [:config :remove-trailing-whitespace?] false) + (assoc-in [:config :remove-consecutive-blank-lines?] false) + (format-text-at-idx))) + +(comment + (:range-text (format-text-at-idx-on-type {:all-text " '([]\n[])" :idx 7}))) + +(defn ^:export infer-indents + "Calculate the edits needed for infering indents in `text`, + and where the cursor should be placed to 'stay' in the right place." + [^js m] + (let [{:keys [text line character previous-line previous-character changes]} (cljify m) + options {:cursorLine line :cursorX character + :prevCursorLine previous-line + :prevCursorX previous-character + :changes (mapv (fn [change] + {:lineNo (:line change) + :x (:character change) + :oldText (:old-text change) + :newText (:new-text change)}) + changes)} + result (cljify (parinfer/smartMode text (jsify options)))] + ;;(println (pr-str options)) + (jsify + (if (:success result) + {:success true + :line (:cursorLine result) + :character (:cursorX result) + :edits (editor/raplacement-edits-for-diffing-lines text (:text result))} + {:success false + :error-msg (get-in result [:error :message])})))) + + + +(comment + (let [o (jsify {:cursorLine 1 :cursorX 4 + :changes {:lineNo 1 :x 0 :oldText "" :newText " "}}) + result (parinfer/parenMode " (defn a []\n (foo []\n (bar)\n (baz)))" o)] + (cljify result)) + + (let [o (jsify {:cursorLine 1 + :cursorX 3 + :prevCursorLine 1 + :prevCursorX 2 + :changes [{:lineNo 1 + :x 2 + :oldText "" + :newText " "}]}) + result (parinfer/parenMode "(comment\n (foo bar\n baz))" o)] + (cljify result)) + + (let [o (jsify {:cursorLine 1 + :cursorX 0 + :prevCursorLine 0 + :prevCursorX 8 + :changes [{:lineNo 0 + :x 8 + :oldText ")" + :newText "\n)"}]}) + result (parinfer/smartMode "(--> foo\n)" o)] + (cljify result)) + + (let [o (jsify {:cursorLine 1 + :cursorX 3 + :prevCursorLine 1 + :prevCursorX 2}) + result (parinfer/parenMode "(comment\n (foo bar\n baz))" o)] + (cljify result)) + + (infer-indents {:text "(comment \n (foo bar \n baz))" + :line 1 + :character 3 + :previous-line 1 + :previous-character 2 + :changes [{:line 1 + :character 2 + :old-text "" + :new-text " "}]}) + + (infer-indents {:text "(comment\n\n (foo bar \n baz))" + :line 1 + :character 0 + :previous-line 1 + :previous-character 0 + :changes [{:line 0 + :character 8 + :old-text "\n (foo bar \n baz))" + :new-text "\n\n (foo bar \n baz))"}]}) + + (infer-indents {:text " (defn a []\n (foo []\n (bar)\n (baz)))" + :line 1 + :character 4 + :changes [{:line 4 + :character 0 + :old-text "" + :new-text " "}]})) diff --git a/src/calva/fmt/inferer.cljs b/src/calva/fmt/inferer.cljs new file mode 100644 index 0000000..1451a57 --- /dev/null +++ b/src/calva/fmt/inferer.cljs @@ -0,0 +1,36 @@ +(ns calva.fmt.inferer + (:require ["parinfer" :as parinfer] + [calva.js-utils :refer [cljify jsify]] + [calva.fmt.editor :as editor])) + +(defn infer-parens + "Calculate the edits needed for infering parens in `text`, + and where the cursor should be placed to 'stay' in the right place." + [^js m] + (let [{:keys [text line character previous-line previous-character]} (cljify m) + options {:cursorLine line + :cursorX character + :prevCursorLine previous-line + :prevCursorX previous-character} + result (cljify (parinfer/smartMode text (jsify options)))] + (jsify + (if (:success result) + {:success true + :line (:cursorLine result) + :character (:cursorX result) + :edits (editor/raplacement-edits-for-diffing-lines text (:text result))} + {:success false + :error-msg (get-in result [:error :message])})))) + + +(comment + (let [o (jsify {:cursorLine 1 :cursorX 13}) + result (parinfer/indentMode " (foo []\n (bar)\n (baz)))" o)] + (cljify result)) + + (infer-parens {:text " (foo []\n (bar)\n (baz)))" + :line 2 + :character 13}) + (infer-parens {:text "(f)))" + :line 0 + :character 2})) \ No newline at end of file diff --git a/src/calva/fmt/playground.cljs b/src/calva/fmt/playground.cljs new file mode 100644 index 0000000..667851c --- /dev/null +++ b/src/calva/fmt/playground.cljs @@ -0,0 +1,145 @@ +(ns calva.fmt.playground + (:require [cljfmt.core :as cljfmt] + #_[zprint.core :refer [zprint-str]] + ["parinfer" :as parinfer] + ["paredit.js" :as paredit] + [calva.js-utils :refer [cljify jsify]] + [calva.fmt.util :as util])) + + +(comment + (cljify (paredit/parse "(\"(\")")) + (cljify (paredit/parse "[]")) + (cljify ((.. paredit -walk -sexpsAt) (paredit/parse "[][]") 3 pr-str)) + (cljify ((.. paredit -walk -containingSexpsAt) (paredit/parse "[][]") 0 pr-str)) + (cljify ((.. paredit -walk -containingSexpsAt) (paredit/parse "([][])") 0 pr-str)) + (-> [{:type "toplevel", :start 0, :end 6, :errors [], :children [{:type "list", :start 0, :end 6, :children [{:type "list", :start 1, :end 3, :children [], :open "[", :close "]"} {:type "list", :start 3, :end 5, :children [], :open "[", :close "]"}], :open "(", :close ")"}]}] + first + :children + count) + (-> (cljify ((.. paredit -walk -containingSexpsAt) (paredit/parse "[][]") "a" identity))) + (-> (cljify ((.. paredit -walk -containingSexpsAt) (paredit/parse "[][]") [0 4] identity))) + (-> (cljify ((.. paredit -walk -containingSexpsAt) (paredit/parse "#()") 0 identity)) + count) + (-> (cljify ((.. paredit -walk -containingSexpsAt) (paredit/parse "()") 0 identity)) + count) + (-> (cljify ((.. paredit -walk -sexpsAt) (paredit/parse "[][]") 0 identity)) + first + :children + count) + (-> (cljify ((.. paredit -walk -sexpsAt) (paredit/parse "([][])") 0 identity)) + first + :children + count) + (-> (cljify ((.. paredit -walk -sexpsAt) (paredit/parse "{[][]}") 0 identity)) + first + :children + first) + (-> (cljify ((.. paredit -walk -sexpsAt) (paredit/parse "\"\"") 0 identity)) + first + :children + count) + (-> (cljify ((.. paredit -walk -sexpsAt) (paredit/parse "\"\"") 0 identity)) + first + :children + first + :type) + (-> (cljify ((.. paredit -walk -sexpsAt) (paredit/parse "'#([])") 3 identity)) + first + :children + (nth 2) + :type) + (-> (cljify ((.. paredit -walk -containingSexpsAt) (paredit/parse "'()") 0 identity)) + first + :children + first + :type) + (cljify (paredit/parse "[][]")) + (cljify (paredit/parse "([][])"))) + +(comment + (foo + ;; + bar + baz)) + +(comment + (cljfmt/reformat-string "(-> foo\nbar\n)\n(foo bar\nbaz\n)" + {:remove-surrounding-whitespace? false + :remove-trailing-whitespace? false + :remove-consecutive-blank-lines? false}) + + (cljfmt/reformat-string "(let [x y\na b]\nbar\n)\n\n(-> foo\nbar\n)\n\n(foo bar\nbaz\n)" + {:remove-surrounding-whitespace? false + :remove-trailing-whitespace? false + :remove-consecutive-blank-lines? false + :indents ^:replace {#".*" [[:inner 0]]}}) + + (cljfmt/reformat-string " '([] +[])" {:remove-surrounding-whitespace? false + :remove-trailing-whitespace? false + :remove-consecutive-blank-lines? false}) + + + (def str "(defn \n\n)") + + (cljfmt/reformat-string str {:remove-surrounding-whitespace? false + :remove-trailing-whitespace? false + :remove-consecutive-blank-lines? false}) + + (cljfmt/reformat-string "(foo + ;; + bar + baz)" + {:remove-surrounding-whitespace? false + :remove-trailing-whitespace? false + :remove-consecutive-blank-lines? false}) + + (cljfmt/reformat-string + "(foo + +)" + {:remove-surrounding-whitespace? false + :remove-trailing-whitespace? false + :indentation? true}) + + (cljfmt/reformat-string "(bar\n \n)" + {:remove-surrounding-whitespace? false + :remove-trailing-whitespace? false}) + + (cljfmt/reformat-string "(ns ui-app.re-frame.db) + +(def default-db #::{:page :home})") + + (cljfmt/reformat-string + "(defn bar\n [x]\n\n baz)") + + (zprint-str "(defn bar\n [x]\n\n baz)" + {:style :community + :parse-string-all? true + :fn-force-nl #{:arg1-body}}) + + (cljfmt/reformat-string + "(defn bar\n [x]\n\n baz)") + + "(defn bar\n [x]\n \n baz)" + + (cljfmt/reformat-string + ";; foo +(defn foo [x] + (* x x)) + 0") + + (div + ;; foo + [:div] + ;; bar + [:div])) + + +(comment + (parinfer/indentMode " (foo [] + (bar) + (baz)))" + (jsify {:cursorLine 2 + :cursorX 13}))) \ No newline at end of file diff --git a/src/calva/fmt/util.cljs b/src/calva/fmt/util.cljs new file mode 100644 index 0000000..0e2426c --- /dev/null +++ b/src/calva/fmt/util.cljs @@ -0,0 +1,56 @@ +(ns calva.fmt.util + (:require [clojure.string] + ["paredit.js" :as paredit] + [calva.js-utils :refer [cljify jsify]])) + + +(defn log + "logs out the object `o` excluding any keywords in `exclude-kws`" + [o & exlude-kws] + (println (pr-str (if (map? o) (apply dissoc o exlude-kws) o))) + o) + + +(defn escape-regexp + "Escapes regexp characters in `s`" + [s] + (clojure.string/replace s #"([.*+?^${}()|\[\]\\])" "\\$1")) + + +(defn current-line + "Finds the text of the current line in `text` from cursor position `index`" + [text index] + (let [head (subs text 0 index) + tail (subs text index)] + (str (second (re-find #"\n?(.*)$" head)) + (second (re-find #"^(.*)\n?" tail))))) + + +(defn re-pos-first + "Find position of first match of `re` in `s`" + [re s] + (if-let [m (.match s re)] + (.-index m) + -1)) + + +(defn split-into-lines + [s] + (clojure.string/split s #"\r?\n" -1)) + + +(defn enclosing? [text] + (let [ast (cljify (paredit/parse text)) + children (:children ast)] + (and (= 1 (count children)) + (= "list" (:type (first children)))))) + +(comment + (enclosing? "[][]") + (enclosing? "([][])") + (enclosing? "([)") + (enclosing? "[\"[\"]") + (enclosing? "(\"[\")") + (enclosing? "\"foo\"") + (enclosing? "\"([.*+?^${}()\n|\\[\\]\\])\"") + (enclosing? "\"([.*+?^${}()\\n|\\\\[\\\\]\\\\])\"")) diff --git a/src/calva/js_utils.cljs b/src/calva/js_utils.cljs new file mode 100644 index 0000000..5a71eb1 --- /dev/null +++ b/src/calva/js_utils.cljs @@ -0,0 +1,7 @@ +(ns calva.js-utils) + +(defn jsify [o] + (clj->js o)) + +(defn cljify [o] + (js->clj o :keywordize-keys true)) \ No newline at end of file diff --git a/src/calva/main.cljs b/src/calva/main.cljs new file mode 100644 index 0000000..e378dda --- /dev/null +++ b/src/calva/main.cljs @@ -0,0 +1,6 @@ +(ns calva.main) + +(defn main [& args] + (js/console.log "hello")) + +(js/console.log "__filename" js/__filename) \ No newline at end of file diff --git a/test/calva/fmt/editor_test.cljs b/test/calva/fmt/editor_test.cljs new file mode 100644 index 0000000..21d094e --- /dev/null +++ b/test/calva/fmt/editor_test.cljs @@ -0,0 +1,13 @@ +(ns calva.fmt.editor-test + (:require [cljs.test :include-macros true :refer [deftest is]] + [calva.fmt.editor :as sut])) + + +(deftest raplacement-edits-for-diffing-lines + (is (= [] + (sut/raplacement-edits-for-diffing-lines "foo\nfoo\nbar\nbar" + "foo\nfoo\nbar\nbar"))) + (is (= [{:edit "replace", :start {:line 1, :character 0}, :end {:line 1, :character 6}, :text "bar"} + {:edit "replace", :start {:line 2, :character 0}, :end {:line 2, :character 3}, :text "baz"}] + (sut/raplacement-edits-for-diffing-lines "foo\nfooooo\nbar\nbar" + "foo\nbar\nbaz\nbar")))) diff --git a/test/calva/fmt/formatter_test.cljs b/test/calva/fmt/formatter_test.cljs new file mode 100644 index 0000000..3b90afc --- /dev/null +++ b/test/calva/fmt/formatter_test.cljs @@ -0,0 +1,238 @@ +(ns calva.fmt.formatter-test + (:require [cljs.test :include-macros true :refer [deftest is]] + [calva.fmt.formatter :as sut])) + +;; TODO: Fix this bug (gazonk should be indented twice) +#_(deftest format-text + (is (= " (foo\n bar\n baz)\n gazonk" + (:range-text (sut/format-text {:range-text " (foo \nbar\n baz)\ngazonk"}))))) + + +(deftest format-text-at-range + (is (= "(foo)\n(defn bar\n [x]\n baz)" + (:range-text (sut/format-text-at-range {:all-text " (foo)\n(defn bar\n[x]\nbaz)" :range [2 26]}))))) + + +(deftest format-text-at-range-old + (is (= "(foo)\n (defn bar\n [x]\n baz)" + (:range-text (sut/format-text-at-range-old {:all-text " (foo)\n(defn bar\n[x]\nbaz)" :range [2 26]}))))) + + +(def all-text " (foo) + (defn bar + [x] + +baz)") + + +(deftest format-text-at-idx + (is (= "(defn bar + [x] + + baz)" + (:range-text (sut/format-text-at-idx {:all-text all-text :idx 11})))) + (is (= 1 + (:new-index (sut/format-text-at-idx {:all-text all-text :idx 11})))) + (is (= [10 38] + (:range (sut/format-text-at-idx {:all-text all-text :idx 11}))))) + + +(deftest new-index + (is (= 1 + (:new-index (sut/format-text-at-idx {:all-text all-text :idx 11})))) + (is (= 13 + (:new-index (sut/format-text-at-idx {:all-text all-text :idx 28})))) + (is (= 10 + (:new-index (sut/format-text-at-idx {:all-text all-text :idx 22})))) + (is (= 12 + (:new-index (sut/format-text-at-idx {:all-text all-text :idx 27})))) + (is (= 22 + (:new-index (sut/format-text-at-idx {:all-text all-text :idx 33})))) + (is (= 5 + (:new-index (sut/format-text-at-idx {:all-text "(defn \n \nfoo)" :idx 6})))) + (is (= 11 + (:new-index (sut/format-text-at-idx {:all-text "(foo\n (bar)\n )" :idx 11}))))) + + +(def head-and-tail-text "(def a 1) + + +(defn foo [x] (let [bar 1] + +bar))") + + +(deftest add-head-and-tail + (is (= {:head "" :tail head-and-tail-text + :all-text head-and-tail-text + :idx 0} + (sut/add-head-and-tail {:all-text head-and-tail-text :idx 0}))) + (is (= {:head head-and-tail-text :tail "" + :all-text head-and-tail-text + :idx (count head-and-tail-text)} + (sut/add-head-and-tail {:all-text head-and-tail-text :idx (count head-and-tail-text)}))) + (is (= {:head "(def a 1)\n\n\n(defn foo " + :tail "[x] (let [bar 1]\n\nbar))" + :all-text head-and-tail-text + :idx 22} + (sut/add-head-and-tail {:all-text head-and-tail-text :idx 22}))) + (is (= {:head head-and-tail-text :tail "" + :all-text head-and-tail-text + :idx (inc (count head-and-tail-text))} + (sut/add-head-and-tail {:all-text head-and-tail-text :idx (inc (count head-and-tail-text))})))) + + +(deftest normalize-indents + (is (= "(foo)\n (defn bar\n [x]\n baz)" + (:range-text (sut/normalize-indents {:all-text " (foo)\n(defn bar\n[x]\nbaz)" + :range [2 26] + :range-text "(foo)\n(defn bar\n [x]\n baz)"}))))) + + +(def first-top-level-text " +;; foo +(defn foo [x] + (* x x)) + ") + +(def mid-top-level-text ";; foo +(defn foo [x] + (* x x)) + +(bar)") + +(def last-top-level-text ";; foo +(defn foo [x] + (* x x)) + ") + + +;; These fail, leading to a horrible behaviour when creating new lines top level +(deftest new-index-top-level + (is (= 1 + (:new-index (sut/format-text-at-idx {:all-text first-top-level-text :idx 1})))) + (is (= first-top-level-text + (:range-text (sut/format-text-at-idx {:all-text first-top-level-text :idx 1})))) + (is (= 32 + (:new-index (sut/format-text-at-idx {:all-text mid-top-level-text :idx 33})))) + (is (= mid-top-level-text + (:range-text (sut/format-text-at-idx {:all-text mid-top-level-text :idx 33})))) + (is (= 32 + (:new-index (sut/format-text-at-idx {:all-text last-top-level-text :idx 32})))) + (is (= last-top-level-text + (:range-text (sut/format-text-at-idx {:all-text last-top-level-text :idx 32}))))) + + +(deftest format-text-at-idx-on-type + (is (= "(bar \n\n )" + (:range-text (sut/format-text-at-idx-on-type {:all-text "(bar \n\n)" :idx 7})))) + (is (= "(bar \n \n )" + (:range-text (sut/format-text-at-idx-on-type {:all-text "(bar \n \n)" :idx 8})))) + (is (= "(bar \n \n )" + (:range-text (sut/format-text-at-idx-on-type {:all-text "(bar \n\n)" :idx 6})))) + (is (= "\"bar \n \n \"" + (:range-text (sut/format-text-at-idx-on-type {:all-text "\"bar \n \n \"" :idx 7})))) + (is (= "\"bar \n \n \"" + (:range-text (sut/format-text-at-idx-on-type {:all-text "\"bar \n \n \"" :idx 7})))) + (is (= "'([]\n [])" + (:range-text (sut/format-text-at-idx-on-type {:all-text " '([]\n[])" :idx 7}))))) + + +(deftest new-index-on-type + (is (= 6 + (:new-index (sut/format-text-at-idx-on-type {:all-text "(defn \n)" :idx 6})))) + (is (= 6 + (:new-index (sut/format-text-at-idx-on-type {:all-text "(defn \n )" :idx 6})))) + (is (= 6 + (:new-index (sut/format-text-at-idx-on-type {:all-text "(defn \n \n )" :idx 6})))) + (is (= 6 + (:new-index (sut/format-text-at-idx-on-type {:all-text "(defn \n\n )" :idx 6})))) + (is (= 11 + (:new-index (sut/format-text-at-idx-on-type {:all-text "(foo\n (bar)\n )" :idx 11}))))) + + +(deftest index-for-tail-in-range + (is (= 7 + (:new-index (sut/index-for-tail-in-range + {:range-text "foo te x t" + :range-tail " x t"})))) + (is (= 169 + (:new-index (sut/index-for-tail-in-range + {:range-text "(create-state \"\" + \"### \" + \" ###\" + \" ### \" + \" # \")" + :range-tail "\" # \")"}))))) + + +(deftest remove-indent-token-if-empty-current-line + (is (= {:range-text "foo\n\nbar" + :range [4 4] + :current-line "" + :new-index 4} + (sut/remove-indent-token-if-empty-current-line {:range-text "foo\n0\nbar" + :range [4 5] + :new-index 4 + :current-line ""}))) + (is (= {:range-text "foo\n0\nbar" + :range [4 5] + :current-line "0" + :new-index 4} + (sut/remove-indent-token-if-empty-current-line {:range-text "foo\n0\nbar" + :range [4 5] + :new-index 4 + :current-line "0"})))) + + +(deftest current-line-empty? + (is (= true (sut/current-line-empty? {:current-line " "}))) + (is (= false (sut/current-line-empty? {:current-line " foo "})))) + + +(deftest indent-before-range + (is (= 10 + (sut/indent-before-range {:all-text "(def a 1) + + +(defn foo [x] (let [bar 1] + +bar))" :range [22 25]}))) + (is (= 4 + (sut/indent-before-range {:all-text " '([] +[])" :range [4 9]})))) + + +(def enclosing-range-text "(def a 1) + + +(defn foo [x] (let [bar 1] + +bar))") + + +(deftest enclosing-range + (is (= [22 25] ;"[x]" + (:range (sut/enclosing-range {:all-text enclosing-range-text :idx 23})))) + (is (= [12 45] ;"enclosing form" + (:range (sut/enclosing-range {:all-text enclosing-range-text :idx 21})))) + (is (= [0 9] ; after top level form + (:range (sut/enclosing-range {:all-text enclosing-range-text :idx 9})))) + (is (= [0 9] ; before top level form + (:range (sut/enclosing-range {:all-text enclosing-range-text :idx 0})))) + (is (= [26 44] ; before top level form + (:range (sut/enclosing-range {:all-text enclosing-range-text :idx 39})))) + (is (= [10 10] ; void (between top level forms) + (:range (sut/enclosing-range {:all-text enclosing-range-text :idx 10})))) + (is (= [5 5] + (:range (sut/enclosing-range {:all-text " []\n \n[]" :idx 5})))) + (is (= [1 7] + (:range (sut/enclosing-range {:all-text " ([][])" :idx 4})))) + (is (= [1 6] + (:range (sut/enclosing-range {:all-text " (\"[\")" :idx 4})))) + (is (= [1 12] + (:range (sut/enclosing-range {:all-text " {:foo :bar}" :idx 2})))) + (is (= [1 13] + (:range (sut/enclosing-range {:all-text " #{:foo :bar}" :idx 8})))) + (is (= [1 12] + (:range (sut/enclosing-range {:all-text " '(:foo bar)" :idx 8}))))) diff --git a/test/calva/fmt/util_test.cljs b/test/calva/fmt/util_test.cljs new file mode 100644 index 0000000..e4ef736 --- /dev/null +++ b/test/calva/fmt/util_test.cljs @@ -0,0 +1,60 @@ +(ns calva.fmt.util-test + (:require [cljs.test :include-macros true :refer [deftest is]] + [calva.fmt.util :as sut])) + + +#_(deftest log + (is (= (with-out-str (sut/log {:range-text ""} :range-text)) + {:range-text ""}))) + + +(def all-text "(def a 1) + + +(defn foo [x] (let [bar 1] + +bar))") + + +(deftest current-line + (is (= "(def a 1)" (sut/current-line all-text 0))) + (is (= "(def a 1)" (sut/current-line all-text 4))) + (is (= "(def a 1)" (sut/current-line all-text 9))) + (is (= "" (sut/current-line all-text 10))) + (is (= "" (sut/current-line all-text 11))) + (is (= "(defn foo [x] (let [bar 1]" (sut/current-line all-text 12))) + (is (= "(defn foo [x] (let [bar 1]" (sut/current-line all-text 27))) + (is (= "(defn foo [x] (let [bar 1]" (sut/current-line all-text 38))) + (is (= "" (sut/current-line all-text 39))) + (is (= "bar))" (sut/current-line all-text (count all-text))))) + + +(deftest re-pos-one + (is (= 6 + (sut/re-pos-first "\\s*x\\s*t$" "foo te x t"))) + (is (= 6 + (sut/re-pos-first "\\s*x\\s*t$" "foo te x t"))) + (is (= 5 + (sut/re-pos-first "\\s*e\\s*xt\\s*$" "foo te xt"))) + (is (= 173 + (sut/re-pos-first "\"\\s*#\\s*\"\\)$" "(create-state \"\" + \"### \" + \" ###\" + \" ### \" + \" # \")")))) + + +(deftest escape-regexp + (is (= "\\.\\*" + (sut/escape-regexp ".*")))) + + +(deftest enclosing? + (is (= false + (sut/enclosing? "[][]"))) + (is (= true + (sut/enclosing? "([][])"))) + (is (= true + (sut/enclosing? "(\"[\")"))) + (is (= true + (sut/enclosing? "(\"(\")")))) \ No newline at end of file