├── .babelrc ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── lib ├── editer.js ├── insert.js ├── remove.js └── utils.js ├── package.json └── test ├── editer_test.js └── fixtures └── sample.txt /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true 3 | } 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | lib 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.12.10" 4 | - "4.0.0" 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Sung Won Cho 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # editer 2 | 3 | [![Build Status](https://travis-ci.org/sungwoncho/editer.svg?branch=master)](https://travis-ci.org/sungwoncho/editer) 4 | 5 | A high level multiline string manipulation library. 6 | 7 | 8 | ## What it does 9 | 10 | Using editer, you can: 11 | 12 | * insert a string before/after a certain line in a multiline string optionally 13 | as a new line 14 | * insert a string before/after nth match of a regex in a multiline string 15 | optionally as a new line 16 | * remove a string from a multiline string before/after a regex matches, 17 | optionally from the same line and/or multiple times. 18 | 19 | ## Installation 20 | 21 | npm install --save editer 22 | 23 | 24 | ## Usage 25 | 26 | Here are some examples of what editer can do. 27 | 28 | 29 | ### Example 1 30 | 31 | Insert a new line after a certain line number. 32 | 33 | ```js 34 | var editer = require('editer'); 35 | var target = 'line 1\nline 3'; 36 | 37 | var result = editer.insert('line 2', target, {after: {line: 1}, asNewLine: true}); 38 | console.log(result); 39 | // => 'line 1\nline 2\nline 3'; 40 | ``` 41 | 42 | ### Example 2 43 | 44 | Insert a string after the second occurrence of regex. 45 | 46 | ```target.js 47 | import create from './create'; 48 | import generate from './generate'; 49 | 50 | export { 51 | create 52 | generate 53 | }; 54 | ``` 55 | 56 | ```js 57 | var fs = require('fs'); 58 | var editer = require('editer'); 59 | var target = fs.readFileSync('./target'); 60 | 61 | var result = editer.insert("import modify from './modify'", target, { 62 | after: { 63 | regex: /import .*$/, 64 | occurrence: 2, 65 | }, 66 | asNewLine: true 67 | }); 68 | console.log(result); 69 | // import create from './create'; 70 | // import generate from './generate'; 71 | // import modify from './modify'; 72 | // 73 | // export { 74 | // create 75 | // generate 76 | // }; 77 | 78 | ``` 79 | 80 | ## API 81 | 82 | ### insert(string, target, options) 83 | 84 | Inserts `string` to `target` at the position specified by `options` and returns 85 | the modified `target`. If `options` fails to find the desired position, returns 86 | `undefined`. 87 | 88 | **string** 89 | 90 | * type: `String` 91 | * a string to be inserted at the target 92 | 93 | **target** 94 | 95 | * type: `String` 96 | * a string to be modified by inserting `string` 97 | 98 | **options** 99 | 100 | * type: `Object` 101 | * an object specifying the position at which the `string` is to be inserted to 102 | `target` and how it should be inserted (e.g. as a new line or a regular line). 103 | 104 | At top level, it can have the following keys: 105 | 106 | * `before` 107 | * `after` 108 | * `or` 109 | * `asNewLine` 110 | 111 | #### before, after 112 | 113 | An object with either `before` or `after` is a 'condition'. A condition cannot 114 | have both `before` and `after`. They take as value an object with following 115 | possible key-value pairs. 116 | 117 | **line** 118 | 119 | * type: `Number` 120 | * the line number in the target 121 | 122 | **regex** 123 | 124 | * type: `RegExp` 125 | * the regex to be matched in the target. You may optionally provide `occurrence` 126 | if providing this option. See below. All regex should have **global** flag, as 127 | Editer uses [lcoater](https://github.com/sungwoncho/locater) internally. 128 | 129 | **occurrence** 130 | 131 | * type: `Number` 132 | * the order of occurrence after which `target` is to be modified. If not 133 | provided, `target` is modified at the first occurrence of the `regex`. 134 | 135 | **last** 136 | 137 | * type: `Boolean` 138 | * if set to true, modify the `target` at last occurrence of the regex. 139 | 140 | *Example* 141 | 142 | ```js 143 | var target = "I love you\nHoney Bunny."; 144 | var options = {before: {regex: /[a-zA-z]{5}/g, occurrence: 2}}; 145 | var result = editer.insert("Nooby ", target, options); 146 | console.log(result); 147 | // => "I love you\nHoney Nooby Bunny." 148 | 149 | var target = "Whoa, whoa, whoa, whoa... stop right there."; 150 | var options = {before: {regex: /whoa/ig, last: true}}; 151 | var result = editer.insert("my god, ", target, options); 152 | console.log(result); 153 | // => "Whoa, whoa, whoa, my god, whoa... stop right there." 154 | ``` 155 | 156 | #### or 157 | 158 | `or` is an array of conditions. Editer attempts to use the conditions 159 | sequentially from the first to the last. If a condition matches, Editer ignores 160 | the rest of the conditions. 161 | 162 | *Example* 163 | 164 | ```js 165 | var target = "Whoa, whoa, whoa, whoa... stop right there."; 166 | var options = {or: [ 167 | {before: {regex: /unicorn/ig, last: true}}, 168 | {after: {regex: /whoa,\s/ig, occurrence: 3}}, 169 | {after: {regex: /stop/i}} 170 | ]}; 171 | var result = editer.insert("hey, ", target, options); 172 | console.log(result); 173 | // => "Whoa, whoa, whoa, hey, whoa... stop right there." 174 | ``` 175 | 176 | #### asNewLine 177 | 178 | * type: `Boolean` 179 | * default: `false` 180 | * insert the `string` as a new line to the `target`. 181 | 182 | *Example* 183 | 184 | ```js 185 | var target = "It's Zed's.\nWho's Zed?"; 186 | var options = {before: {regex: /Zed.*\n/g}, asNewLine: true}; 187 | var result = editer.insert("...", target, options); 188 | console.log(result); 189 | // => "It's \n...\nZed's.\nWho's Zed?" 190 | ``` 191 | 192 | ### remove(string, target, options) 193 | 194 | Removes `string` from the `target` at the position specified by `options`. 195 | 196 | **string** 197 | 198 | * type: `String` 199 | * a string to be removed from the target 200 | 201 | **target** 202 | 203 | * type: `String` 204 | * a string to be modified by removing `string` 205 | 206 | **options** 207 | 208 | * type: `Object` 209 | * an object specifying the position at which the `string` is to be removed from 210 | `target`. 211 | 212 | At top level, it can have the following keys: 213 | 214 | * `before` 215 | * `after` 216 | * `or` 217 | * `multi` 218 | * `onSameLine` 219 | 220 | All `before`, `after`, and `or` options are similar to those of `insert` API. 221 | The only difference is that here they support only regex, not line number. 222 | 223 | #### multi 224 | 225 | * type: `Boolean` 226 | * default: `false` 227 | * Remove all occurrences of the `string` in the section of the `target` scoped 228 | by the condition, and other options such as `onSameLine`. 229 | 230 | #### onSameLine 231 | 232 | * type: `Boolean` 233 | * default: `false` 234 | * Scope the removal to the same line as the match of the regex in the target. 235 | 236 | 237 | ## License 238 | 239 | MIT 240 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/editer'); 2 | -------------------------------------------------------------------------------- /lib/editer.js: -------------------------------------------------------------------------------- 1 | import insertAPI from './insert'; 2 | import removeAPI from './remove'; 3 | 4 | export default { 5 | insert: insertAPI, 6 | remove: removeAPI 7 | }; 8 | 9 | export const insert = insertAPI; 10 | export const remove = removeAPI; 11 | -------------------------------------------------------------------------------- /lib/insert.js: -------------------------------------------------------------------------------- 1 | import getTargetInfo from './utils'; 2 | 3 | const lineBreak = '\n'; 4 | 5 | export default function insert(string, target, options) { 6 | 7 | /** 8 | * Get the final string to insert into the target, optionally inserting a 9 | * line break before and after depending on whether it should be a new line, 10 | * and whether there are already line breaks before and after the string 11 | * 12 | * @param nextChar {String} - character that will come after the string after 13 | * it is inserted to the target 14 | * @param prevChar {String} - character that will come before the string 15 | * after it is inserted to the target 16 | * @return {String} - a modifier that will be inserted to the target 17 | */ 18 | function getModifier(nextChar, prevChar, condition) { 19 | if (! condition.asNewLine) { 20 | return string; 21 | } 22 | 23 | if (nextChar && nextChar !== lineBreak) { 24 | string = string + '\n'; 25 | } 26 | if (prevChar && prevChar !== lineBreak) { 27 | string = '\n' + string; 28 | } 29 | if (condition._appendToModifier) { 30 | string = string + condition._appendToModifier; 31 | } 32 | if (condition._prependToModifier) { 33 | string = condition._prependToModifier + string; 34 | } 35 | 36 | return string; 37 | } 38 | 39 | /** 40 | * Insert the modifier to the target at the desired position, and returns the 41 | * resulting, modified target 42 | * 43 | * @param targetIndex {Number} - index in the target string at which the 44 | * modifier is to be inserted 45 | * @return {String} - a modified target 46 | */ 47 | function getModifiedTarget(targetIndex, condition) { 48 | return target.slice(0, targetIndex) + 49 | getModifier(target[targetIndex], target[targetIndex - 1], condition) + 50 | target.slice(targetIndex); 51 | } 52 | 53 | if (options.or) { 54 | for (let i = 0; i < options.or.length; i++) { 55 | let targetInfo = getTargetInfo(target, options.or[i]); 56 | if (targetInfo) { 57 | return getModifiedTarget(targetInfo.index, options.or[i]); 58 | } 59 | } 60 | } else { 61 | let targetInfo = getTargetInfo(target, options); 62 | return getModifiedTarget(targetInfo.index, options); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/remove.js: -------------------------------------------------------------------------------- 1 | import locater from 'locater'; 2 | import getTargetInfo from './utils'; 3 | import _ from 'lodash'; 4 | 5 | const lineBreak = '\n'; 6 | 7 | function getNthLine(string, lineNumber) { 8 | let arr = string.split(lineBreak); 9 | return arr[lineNumber - 1]; 10 | } 11 | 12 | function replaceNthLine(string, lineNumber, newString) { 13 | let arr = string.split(lineBreak); 14 | arr[lineNumber - 1] = newString; 15 | 16 | return arr.join('\n'); 17 | } 18 | 19 | export default function remove(string, target, options) { 20 | 21 | function getModifiedTarget(targetInfo, condition) { 22 | let targetSubstring; 23 | 24 | if (condition.onSameLine) { 25 | targetSubstring = getNthLine(target, targetInfo.line); 26 | } else { 27 | if (condition.after) { 28 | targetSubstring = target.slice(targetInfo.index); 29 | } else if (condition.before) { 30 | targetSubstring = target.slice(0, targetInfo.index); 31 | } 32 | } 33 | 34 | let regex; 35 | 36 | if (condition.multi) { 37 | regex = new RegExp(_.escapeRegExp(string), 'g'); 38 | } else { 39 | regex = new RegExp(_.escapeRegExp(string)); 40 | } 41 | 42 | let newSubstring = targetSubstring.replace(regex, ''); 43 | 44 | if (condition.onSameLine) { 45 | return replaceNthLine(target, targetInfo.line, newSubstring); 46 | } else { 47 | return target.replace(targetSubstring, newSubstring); 48 | } 49 | } 50 | 51 | if (options.or) { 52 | for (let i = 0; i < options.or.length; i++) { 53 | let targetInfo = getTargetInfo(target, options.or[i]); 54 | if (targetInfo) { 55 | return getModifiedTarget(targetInfo, options.or[i]); 56 | } 57 | } 58 | } else { 59 | let targetInfo = getTargetInfo(target, options); 60 | return getModifiedTarget(targetInfo, options); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | import locater from 'locater'; 2 | import _ from 'lodash'; 3 | 4 | const lineBreak = '\n'; 5 | 6 | export default function getTargetInfo(target, condition) { 7 | if (condition.after) { 8 | if (condition.after.line) { 9 | let newLinePos = locater.find('\n', target, {getGlobalIndices: true}); 10 | if (!newLinePos.length) { 11 | return; 12 | } 13 | let targetNewLinePos = _.find(newLinePos, {line: condition.after.line}); 14 | let targetIndex = targetNewLinePos.globalIndex; 15 | 16 | return { 17 | index: targetIndex, 18 | line: targetNewLinePos.line 19 | }; 20 | } 21 | 22 | if (condition.after.regex) { 23 | let matches = locater.find(condition.after.regex, target, { 24 | getGlobalIndices: true, 25 | getMatches: true 26 | }); 27 | if (!matches.length) { 28 | return; 29 | } 30 | let targetMatchPos, targetIndex; 31 | 32 | if (condition.after.occurrence) { 33 | targetMatchPos = matches[condition.after.occurrence - 1]; 34 | } else if (condition.after.last) { 35 | targetMatchPos = matches[matches.length - 1]; 36 | } else { 37 | targetMatchPos = matches[0]; 38 | } 39 | 40 | if (targetMatchPos.match.slice(-1) === lineBreak) { 41 | targetIndex = targetMatchPos.globalIndex + targetMatchPos.match.length - 1; 42 | } else { 43 | targetIndex = targetMatchPos.globalIndex + targetMatchPos.match.length; 44 | } 45 | 46 | if (! targetIndex) { 47 | return; 48 | } else { 49 | return { 50 | index: targetIndex, 51 | line: targetMatchPos.line 52 | }; 53 | } 54 | } 55 | } 56 | 57 | if (condition.before) { 58 | if (condition.before.line) { 59 | let newLinePos = locater.find('\n', target, {getGlobalIndices: true}); 60 | if (!newLinePos.length) { 61 | return; 62 | } 63 | let targetNewLinePos = _.find(newLinePos, {line: condition.before.line - 1}); 64 | let targetIndex; 65 | 66 | if (targetNewLinePos) { 67 | return { 68 | index: targetNewLinePos.globalIndex + 1, 69 | line: targetNewLinePos.line 70 | }; 71 | } else { 72 | return { 73 | index: 0, 74 | line: 0 75 | }; 76 | } 77 | } 78 | 79 | if (condition.before.regex) { 80 | let matches = locater.find(condition.before.regex, target, { 81 | getGlobalIndices: true, 82 | getMatches: true 83 | }); 84 | if (!matches.length) { 85 | return; 86 | } 87 | let targetMatchPos, targetIndex; 88 | 89 | if (condition.before.occurrence) { 90 | targetMatchPos = matches[condition.before.occurrence - 1]; 91 | } else if (condition.before.last) { 92 | targetMatchPos = matches[matches.length - 1]; 93 | } else { 94 | targetMatchPos = matches[0]; 95 | } 96 | 97 | targetIndex = targetMatchPos.globalIndex; 98 | 99 | return { 100 | index: targetIndex, 101 | line: targetMatchPos.line 102 | }; 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "editer", 3 | "version": "0.4.0", 4 | "description": "A high level multiline string manipulation library", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npm run-script compile && mocha ./test", 8 | "compile": "./node_modules/.bin/babel lib/ -d dist/", 9 | "prepublish": "npm run-script compile" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/sungwoncho/editer.git" 14 | }, 15 | "keywords": [ 16 | "string", 17 | "manipulation" 18 | ], 19 | "author": "Sung Won Cho (https://sungwoncho.io/)", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/sungwoncho/editer/issues" 23 | }, 24 | "homepage": "https://github.com/sungwoncho/editer#readme", 25 | "devDependencies": { 26 | "babel-cli": "^6.5.1", 27 | "babel-preset-es2015": "^6.5.0", 28 | "chai": "^3.5.0", 29 | "mocha": "^2.4.5" 30 | }, 31 | "dependencies": { 32 | "locater": "^1.3.0", 33 | "lodash": "^4.5.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/editer_test.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var expect = require('chai').expect; 3 | var editer = require('../dist/editer'); 4 | 5 | describe(".insert", function() { 6 | describe("after.line", function() { 7 | it("inserts a string to target after the line", function() { 8 | var target = 'line 1\nline 3'; 9 | var result = editer.insert(' extended', target, {after: {line: 1}}); 10 | 11 | expect(result).to.equal('line 1 extended\nline 3'); 12 | }); 13 | }); 14 | 15 | describe("after.line, asNewLine", function() { 16 | it("inserts a string to target after the line as a new line", function() { 17 | var target = 'line 1\nline 3'; 18 | var result = editer.insert('line 2', target, {after: {line: 1}, asNewLine: true}); 19 | 20 | expect(result).to.equal('line 1\nline 2\nline 3'); 21 | }); 22 | 23 | it("inserts a string to target after the last line as a new line", function() { 24 | var target = 'line 1\nline 2'; 25 | var result = editer.insert('line 3', target, {after: {line: 2}, asNewLine: true}); 26 | 27 | expect(result).to.equal('line 1\nline 2\nline 3'); 28 | }); 29 | }); 30 | 31 | describe("before.line", function() { 32 | it("inserts a string to target before the line", function() { 33 | var target = 'line 1\nline 2\nline 3'; 34 | var result = editer.insert('this is ', target, {before: {line: 2}}); 35 | 36 | expect(result).to.equal('line 1\nthis is line 2\nline 3'); 37 | }); 38 | 39 | it("can insert a string to the target before the line if the first line", function() { 40 | var target = 'line 1\nline 2\nline 3'; 41 | var result = editer.insert('this is ', target, {before: {line: 1}}); 42 | 43 | expect(result).to.equal('this is line 1\nline 2\nline 3'); 44 | }); 45 | }); 46 | 47 | describe("before.line asNewLine", function() { 48 | it("inserts a string to target before the line as a new line", function() { 49 | var target = 'line 0\nline 2\nline 3'; 50 | var result = editer.insert('line 1', target, {before: {line: 2}, asNewLine: true}); 51 | 52 | expect(result).to.equal('line 0\nline 1\nline 2\nline 3'); 53 | }); 54 | 55 | it("inserts a string to target before the first line as a new line", function() { 56 | var target = 'line 1\nline 2\nline 3'; 57 | var result = editer.insert('line 0', target, {before: {line: 1}, asNewLine: true}); 58 | 59 | expect(result).to.equal('line 0\nline 1\nline 2\nline 3'); 60 | }); 61 | }); 62 | 63 | describe("after.regex", function() { 64 | it("inserts a string to target after the first match of regex ending with newline", function() { 65 | var target = "It's Zed's.\nWho's Zed?"; 66 | var result = editer.insert(' Baby.', target, {after: {regex: /Zed.*\n/g}}); 67 | 68 | expect(result).to.equal("It's Zed's. Baby.\nWho's Zed?"); 69 | }); 70 | 71 | it("inserts a string to target after the first match of regex", function() { 72 | var target = "It's Zed's.\nWho's Zed?"; 73 | var result = editer.insert("oo", target, {after: {regex: /Zed/g}}); 74 | 75 | expect(result).to.equal("It's Zedoo's.\nWho's Zed?"); 76 | }); 77 | }); 78 | 79 | describe("after.regex after.occurrence", function() { 80 | it("inserts a string to target after the nth match", function() { 81 | var target = "It's Zed's.\nWho's Zed?"; 82 | var options = {after: {regex: /Zed/g, occurrence: 2}}; 83 | var result = editer.insert("oo", target, options); 84 | 85 | expect(result).to.equal("It's Zed's.\nWho's Zedoo?"); 86 | }); 87 | }); 88 | 89 | describe("after.regex after.last", function() { 90 | it("inserts a string to target after the last match", function() { 91 | var target = "Whoa, whoa, whoa, whoa... stop right there."; 92 | var options = {after: {regex: /whoa/ig, last: true}}; 93 | var result = editer.insert(", aww", target, options); 94 | 95 | expect(result).to.equal("Whoa, whoa, whoa, whoa, aww... stop right there."); 96 | }); 97 | }); 98 | 99 | describe("after.regex asNewLine", function() { 100 | it("inserts a string to target after the first match of regex as a new line", function() { 101 | var target = "I love you\nHoney Bunny."; 102 | var result = editer.insert('Nooby ', target, {after: {regex: /[a-zA-z]{5}\s/g}, asNewLine: true}); 103 | 104 | expect(result).to.equal("I love you\nHoney \nNooby \nBunny."); 105 | }); 106 | }); 107 | 108 | describe("after.regex after.occurrence asNewLine", function() { 109 | it("inserts a string to target after the nth occurrence of regex as a new line", function() { 110 | var target = "I love you\nHoney Bunny Babby."; 111 | var options = {after: {regex: /[a-zA-z]{5}/g, occurrence: 2}, asNewLine: true}; 112 | var result = editer.insert('Nooby', target, options); 113 | 114 | expect(result).to.equal("I love you\nHoney Bunny\nNooby\n Babby."); 115 | }); 116 | }); 117 | 118 | describe("before.regex", function() { 119 | it("inserts a string to target before the first match of regex", function() { 120 | var target = "It's Zed's.\nWho's Zed?"; 121 | var result = editer.insert("... ", target, {before: {regex: /Zed.*\n/g}}); 122 | 123 | expect(result).to.equal("It's ... Zed's.\nWho's Zed?"); 124 | }); 125 | }); 126 | 127 | describe("before.regex asNewLine", function() { 128 | it("inserts a string to target before the first match of regex as a new line", function() { 129 | var target = "It's Zed's.\nWho's Zed?"; 130 | var options = {before: {regex: /Zed.*\n/g}, asNewLine: true}; 131 | var result = editer.insert("...", target, options); 132 | 133 | expect(result).to.equal("It's \n...\nZed's.\nWho's Zed?"); 134 | }); 135 | }); 136 | 137 | describe("before.regex before.occurrence", function() { 138 | it("inserts a string to target before the nth match of regex", function() { 139 | var target = "I love you\nHoney Bunny."; 140 | var options = {before: {regex: /[a-zA-z]{5}/g, occurrence: 2}}; 141 | var result = editer.insert("Nooby ", target, options); 142 | 143 | expect(result).to.equal("I love you\nHoney Nooby Bunny."); 144 | }); 145 | }); 146 | 147 | describe("before.regex before.occurrence asNewLine", function() { 148 | it("inserts a string to target before the nth match of regex as a new line", function() { 149 | var target = "I love you\nHoney Bunny."; 150 | var options = {before: {regex: /[a-zA-z]{5}/g, occurrence: 2}, asNewLine: true}; 151 | var result = editer.insert("Nooby", target, options); 152 | 153 | expect(result).to.equal("I love you\nHoney \nNooby\nBunny."); 154 | }); 155 | }); 156 | 157 | describe("before.regex before.last", function() { 158 | it("inserts a string to target before the last match of regex", function() { 159 | var target = "Whoa, whoa, whoa, whoa... stop right there."; 160 | var options = {before: {regex: /whoa/ig, last: true}}; 161 | var result = editer.insert("my god, ", target, options); 162 | 163 | expect(result).to.equal("Whoa, whoa, whoa, my god, whoa... stop right there."); 164 | }); 165 | }); 166 | 167 | describe("or", function() { 168 | it("performs the second operation if the first one's condition does not yield match", function() { 169 | var target = "Whoa, whoa, whoa, whoa... stop right there."; 170 | var options = {or: [ 171 | {before: {regex: /unicorn/ig, last: true}}, 172 | {after: {regex: /whoa,\s/ig, occurrence: 3}}, 173 | {after: {regex: /stop/i}} 174 | ]}; 175 | var result = editer.insert("hey, ", target, options); 176 | 177 | expect(result).to.equal("Whoa, whoa, whoa, hey, whoa... stop right there."); 178 | }); 179 | 180 | it("can pass asNewLine option within a condition in 'or'", function() { 181 | var target = "Whoa, whoa, whoa, whoa... stop right there."; 182 | var options = {or: [ 183 | {before: {regex: /unicorn/ig, last: true}}, 184 | {after: {regex: /whoa\.\.\.\s/ig}, asNewLine: true}, 185 | ]}; 186 | var result = editer.insert("hey.", target, options); 187 | 188 | expect(result).to.equal("Whoa, whoa, whoa, whoa... \nhey.\nstop right there."); 189 | }); 190 | }); 191 | }); 192 | 193 | describe(".remove", function() { 194 | describe("after.regex", function() { 195 | it("removes a string in the target after the regex", function() { 196 | var target = 'I love you\n Honey Bunny, , , \nNooby.'; 197 | var result = editer.remove(',', target, {after: {regex: /Bunny/g}, multi: false}); 198 | 199 | expect(result).to.equal('I love you\n Honey Bunny , , \nNooby.'); 200 | }); 201 | }); 202 | 203 | describe("after.regex multi", function() { 204 | it("removes all occurrences of the string in the target after the regex", function() { 205 | var target = 'Hey Charlie you asleep?\n Charlie are you asleep?'; 206 | var result = editer.remove(' asleep', target, {after: {regex: /Hey/g}, multi: true}); 207 | 208 | expect(result).to.equal('Hey Charlie you?\n Charlie are you?'); 209 | }); 210 | }); 211 | 212 | describe("after.regex onSameline", function() { 213 | it("does not remove the string if outside the line where the regex matches", function() { 214 | var target = 'I love you\n Honey Bunny\n Bobby Nooby'; 215 | var result = editer.remove('Bobby', target, {after: {regex: /Honey/g}, onSameLine: true}); 216 | 217 | expect(result).to.equal('I love you\n Honey Bunny\n Bobby Nooby'); 218 | }); 219 | 220 | it("removes a string in the target before the regex match if on the same line", function() { 221 | var target = 'I love you\n Honey Bunny Bobby Nooby'; 222 | var result = editer.remove('Bobby ', target, {after: {regex: /Honey/g}, onSameLine: true, multi: false}); 223 | 224 | expect(result).to.equal('I love you\n Honey Bunny Nooby'); 225 | }); 226 | }); 227 | 228 | describe("after.regex onSameLine multi", function() { 229 | it("removes all occurrences of the string in the target after the regex on the same line", function() { 230 | var target = 'Hey Charlie you asleep hey hey?\n Charlie are you asleep?'; 231 | var result = editer.remove(' hey', target, {after: {regex: /Hey/g}, onSameLine: true, multi: true}); 232 | 233 | expect(result).to.equal('Hey Charlie you asleep?\n Charlie are you asleep?'); 234 | }); 235 | }); 236 | 237 | describe("before.regex", function() { 238 | it("removes a string in the target before the regex", function() { 239 | var target = 'I love you\n Honey, Bunny'; 240 | var result = editer.remove(',', target, {before: {regex: /Bunny/g}, onSameLine: false, multi: false}); 241 | 242 | expect(result).to.equal('I love you\n Honey Bunny'); 243 | }); 244 | }); 245 | 246 | describe("before.regex", function() { 247 | it("does not do anything if there is no match", function() { 248 | var target = 'I love you\n Honey, Bunny'; 249 | var result = editer.remove('unicorn', target, {before: {regex: /Bunny/g}, onSameLine: false, multi: false}); 250 | 251 | expect(result).to.equal('I love you\n Honey, Bunny'); 252 | }); 253 | }); 254 | 255 | describe("before.regex multi", function() { 256 | it("removes all occurrences of the string in the target before the regex", function() { 257 | var target = 'Hey Charlie you asleep asleep?\nYo Charlie are you asleep?'; 258 | var result = editer.remove(' asleep', target, {before: {regex: /Yo/g}, onSameLine: false, multi: true}); 259 | 260 | expect(result).to.equal('Hey Charlie you?\nYo Charlie are you asleep?'); 261 | }); 262 | }); 263 | 264 | describe("before.regex onSameLine", function() { 265 | it("does not remove a string in the target before the regex match if not on the same line", function() { 266 | var target = 'I love you\n Honey, Bunny'; 267 | var result = editer.remove('you', target, {before: {regex: /Bunny/g}, onSameLine: true, multi: false}); 268 | 269 | expect(result).to.equal('I love you\n Honey, Bunny'); 270 | }); 271 | 272 | it("removes a string in the target before the regex match if on the same line", function() { 273 | var target = 'I love\nyou Honey, Bunny'; 274 | var result = editer.remove('you', target, {before: {regex: /Bunny/g}, onSameLine: true, multi: false}); 275 | 276 | expect(result).to.equal('I love\n Honey, Bunny'); 277 | }); 278 | }); 279 | 280 | 281 | describe("before.regex onSameLine multi", function() { 282 | it("removes all occurrences of the string in the target before the regex on the smae line", function() { 283 | var target = 'Hey Charlie you asleep asleep? Hey, Charlie.\nHey, Hey, Yo, Charlie are you asleep?'; 284 | var result = editer.remove('Hey, ', target, {before: {regex: /Yo/g}, onSameLine: true, multi: true}); 285 | 286 | expect(result).to.equal('Hey Charlie you asleep asleep? Hey, Charlie.\nYo, Charlie are you asleep?'); 287 | }); 288 | }); 289 | }); 290 | -------------------------------------------------------------------------------- /test/fixtures/sample.txt: -------------------------------------------------------------------------------- 1 | Whose motorcycle is this? 2 | It's a chopper, baby. 3 | Whose chopper is this? 4 | It's Zed's. 5 | Who's Zed? 6 | Zed's dead, baby. Zed's dead. 7 | --------------------------------------------------------------------------------