Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Overhaul snippet syntax for feature support #308

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 10 additions & 13 deletions lib/insertion.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,16 @@ function transformText (str, flags) {
}

class Insertion {
constructor ({ range, substitution }) {
constructor ({ range, transformation }) {
this.range = range
this.substitution = substitution
if (substitution) {
if (substitution.replace === undefined) {
substitution.replace = ''
}
this.replacer = this.makeReplacer(substitution.replace)
this.transformation = transformation
if (transformation) {
this.replacer = this.makeReplacer(transformation.replace)
}
}

isTransformation () {
return !!this.substitution
return !!this.transformation
}

makeReplacer (replace) {
Expand All @@ -73,8 +70,8 @@ class Insertion {
replace.forEach(token => {
if (typeof token === 'string') {
result.push(transformText(token, flags))
} else if (token.escape) {
ESCAPES[token.escape](flags, result)
} else if (token.modifier) {
ESCAPES[token.modifier](flags, result)
} else if (token.backreference) {
let transformed = transformText(match[token.backreference], flags)
result.push(transformed)
Expand All @@ -85,9 +82,9 @@ class Insertion {
}

transform (input) {
let { substitution } = this
if (!substitution) { return input }
return input.replace(substitution.find, this.replacer)
let { transformation } = this
if (!transformation) { return input }
return input.replace(transformation.find, this.replacer)
}
}

Expand Down
82 changes: 82 additions & 0 deletions lib/snippet-body-old.pegjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
// Joins all consecutive strings in a collection without clobbering any
// non-string members.
function coalesce (parts) {
const result = [];
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
const ri = result.length - 1;
if (typeof part === 'string' && typeof result[ri] === 'string') {
result[ri] = result[ri] + part;
} else {
result.push(part);
}
}
return result;
}

function flatten (parts) {
return parts.reduce(function (flat, rest) {
return flat.concat(Array.isArray(rest) ? flatten(rest) : rest);
}, []);
}
}
bodyContent = content:(tabStop / bodyContentText)* { return content; }
bodyContentText = text:bodyContentChar+ { return text.join(''); }
bodyContentChar = escaped / !tabStop char:. { return char; }

escaped = '\\' char:. { return char; }
tabStop = tabStopWithTransformation / tabStopWithPlaceholder / tabStopWithoutPlaceholder / simpleTabStop

simpleTabStop = '$' index:[0-9]+ {
return { index: parseInt(index.join("")), content: [] };
}
tabStopWithoutPlaceholder = '${' index:[0-9]+ '}' {
return { index: parseInt(index.join("")), content: [] };
}
tabStopWithPlaceholder = '${' index:[0-9]+ ':' content:placeholderContent '}' {
return { index: parseInt(index.join("")), content: content };
}
tabStopWithTransformation = '${' index:[0-9]+ transformation:transformationSubstitution '}' {
return {
index: parseInt(index.join(""), 10),
content: [],
transformation,
};
}

placeholderContent = content:(tabStop / placeholderContentText / variable )* { return flatten(content); }
placeholderContentText = text:placeholderContentChar+ { return coalesce(text); }
placeholderContentChar = escaped / placeholderVariableReference / !tabStop !variable char:[^}] { return char; }

placeholderVariableReference = '$' digit:[0-9]+ {
return { index: parseInt(digit.join(""), 10), content: [] };
}

variable = '${' variableContent '}' {
return ''; // we eat variables and do nothing with them for now
}
variableContent = content:(variable / variableContentText)* { return content; }
variableContentText = text:variableContentChar+ { return text.join(''); }
variableContentChar = !variable char:('\\}' / [^}]) { return char; }

escapedForwardSlash = pair:'\\/' { return pair; }

// A pattern and replacement for a transformed tab stop.
transformationSubstitution = '/' find:(escapedForwardSlash / [^/])* '/' replace:formatString* '/' flags:[imy]* {
let reFind = new RegExp(find.join(''), flags.join('') + 'g');
return { find: reFind, replace: replace[0] };
}

formatString = content:(formatStringEscape / formatStringReference / escapedForwardSlash / [^/])+ {
return content;
}
// Backreferencing a transformation capture group. Different from a tab stop.
formatStringReference = '$' digits:[0-9]+ {
return { backreference: parseInt(digits.join(''), 10) };
};
// One of the special control flags in a format string for case folding and
// other tasks.
formatStringEscape = '\\' flag:[ULulErn$] {
return { modifier: flag };
}
7 changes: 5 additions & 2 deletions lib/snippet-body-parser.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
const syntax = atom.config.get("snippets.snippetSyntax");
const parserName = syntax === "original" ? "snippet-body-old" : "snippet-body";

let parser
try {
parser = require('./snippet-body')
parser = require(`./${parserName}`)
} catch (error) {
const {allowUnsafeEval} = require('loophole')
const fs = require('fs-plus')
const PEG = require('pegjs')

const grammarSrc = fs.readFileSync(require.resolve('./snippet-body.pegjs'), 'utf8')
const grammarSrc = fs.readFileSync(require.resolve(`./${parserName}.pegjs`), 'utf8')
parser = null
allowUnsafeEval(() => parser = PEG.buildParser(grammarSrc))
}
Expand Down
Loading