Multiline String Manipulation with Editer

Sung Won Cho

Presented on March 3 at Sydney Node.ninjas

Structure

  1. API Overview
  2. Motivating Example
  3. Post Mortem

API Overview

Insert a string before/after a certain line in a multiline string optionally as a new line

Example

sample.txt
This is line 1
This is line 3
This is line 4
  

sample.js
import fs from 'fs';
import editer from 'editer';

let content = fs.readFileSync('./sample.txt');
editer.insert('This is line 2', content, {
  after: {line: 1}, asNewLine: true
});
// =>
// This is line 1
// This is line 2
// This is line 3
// This is line 4
  

Insert a string before/after nth match of a regex in a multiline string optionally as a new line

Example

sample.txt
Frank: Hey Charlie you asleep?
Frank: Charlie are you asleep?
Charlie: no NO.
  

sample.js
import fs from 'fs';
import editer from 'editer';

let content = fs.readFileSync('./sample.txt');
editer.insert(' dude', content, {
  after: {regex: /Charlie/}
});
editer.insert('Dude, ', content, {
  before: {regex: /Charlie/, occurrence: 2}
});
editer.insert('no.', content, {
  after: {regex: /asleep\?/, last: true, asNewLine: true}
});
  

Insert with multiple conditions with 'or'

Example

sample.txt
Whoa, whoa, whoa, whoa... stop right there.
  

sample.js
import fs from 'fs';
import editer from 'editer';

let content = fs.readFileSync('./sample.txt');
editer.insert('hey, ', content, {or: [
  {before: {regex: /unicorn/ig, last: true}},
  {after: {regex: /whoa,\s/ig, occurrence: 3}},
  {after: {regex: /stop/i}, asNewLine: true}
]});
  

Remove a string from a multiline string before/after a regex matches, optionally from the same line and/or multiple times.

Example

sample.txt
Rum ham! Rum ham!
I'm sorry rum ham! I'm sorry...
  

sample.js
import fs from 'fs';
import editer from 'editer';

let content = fs.readFileSync('./sample.txt');
editer.remove(' rum ham', {after: {regex: /Rum Ham\!/g}});
// =>
// Rum ham! Rum ham!
// I'm sorry! I'm sorry...
  

Example 2

sample.txt
Rum ham! Rum ham!
I'm sorry rum ham! I'm sorry...
  

sample.js
import fs from 'fs';
import editer from 'editer';

let content = fs.readFileSync('./sample.txt');
editer.remove(' rum ham', {
  after: {regex: /Rum Ham\!/g}, onSameLine: true
});
editer.remove(' ham', {
  after: {regex: /Rum/g}, multi: true
});
editer.remove(' ham', {
  after: {regex: /Rum/g}, multi: true, onSameLine: true
});
  

Motivating Example

sample.js
import users from './users';
import posts from './posts';
// import comments from './comments';

export default {
  users,
  posts,
  // comments
};
  

solution-1.js
import fs from 'fs';
import editer from 'editer';

let content = fs.readFileSync('./sample.js');
editer.insert("import comments from './comments'", content {
  or: [
    {after: {regex: /import .*\n/g, last: true}, asNewLine: true},
    {before: {line: 1}, asNewLine: true, _appendToModifier: '\n'}
  ]
});
  

outputs:

result
import users from './users';
import posts from './posts';
import comments from './comments';

export default {
  users,
  posts
};
  

Locater

sample.txt
In my younger and more vulnerable years
my father gave me some advice that
I've been turning over in my mind ever since.

example.js
import fs from 'fs';
import locater from 'locater';

let content = fs.readFileSync('./sample.txt');
locater.find('my', sample);
// => [{ line: 1, cursor: 4 }, { line: 2, cursor: 1 },
//     { line: 3, cursor: 27 }]

Match Bracket

sample.js
function identity(arg) {
  if (arg) { // (comment) }}}
    return arg;
  }
};

example.js
import fs from 'fs';
import matchBracket from 'match-bracket';

let content = fs.readFileSync('./sample.js');
matchBracket(content, {line: 1, cursor: 24});
// => {line: 5, cursor: 1}

content
import users from './users';
import posts from './posts';
import comments from './comments';

export default {
  users,
  posts
};
  

solution-2.js
let beginningPosition = locater.findOne('export default {', content);
let closingBracketPosition = matchBracket(
  content,
  _.assign(beginningPosition, {cursor: 16})
);

editer.insert('comments', content, {
  before: {line: closingBracketPosition.line},
  asNewLine: true
});

if (beginningPosition.line !== closingBracketPosition.line-1) {
  editer.insert(',', content, {
    before: {line: closingBracketPosition.line},
  });
}
  

Result

sample.js
import users from './users';
import posts from './posts';
import comments from './comments';

export default {
  users,
  posts,
  comments
};
  

Post Mortem

1. Eat your own dog food

2. Write tiny libraries that specialize in one thing

3. Open source your solutions

Thanks

Sung Won Cho