.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / style-search / index.js
1 var SKIP = 'skip';
2 var CHECK = 'check';
3 var ONLY = 'only';
4
5 module.exports = function (options, callback) {
6   var source = options.source;
7   var target = options.target;
8
9   var skipComments = (options.comments) ? options.comments === SKIP : true;
10   var skipStrings = (options.strings) ? options.strings === SKIP : true;
11   var skipFunctionNames = (options.functionNames) ? options.functionNames === SKIP : true;
12   var skipFunctionArguments = options.functionArguments === SKIP;
13   var skipParentheticals = options.parentheticals === SKIP;
14
15   var onceOptionUsed = false;
16   Object.keys(options).forEach(function(key) {
17     if (options[key] !== ONLY) return;
18     if (!onceOptionUsed) {
19       onceOptionUsed = true;
20     } else {
21       throw new Error('Only one syntax feature option can be the "only" one to check');
22     }
23   });
24
25   var onlyComments = options.comments === ONLY;
26   var onlyStrings = options.strings === ONLY;
27   var onlyFunctionNames = options.functionNames === ONLY;
28   var onlyFunctionArguments = options.functionArguments === ONLY;
29   var onlyParentheticals = options.parentheticals === ONLY;
30
31   var insideString = false;
32   var insideComment = false;
33   var insideSingleLineComment = false;
34   var insideParens = false;
35   var insideFunctionArguments = false;
36   var openingParenCount = 0;
37   var matchCount = 0;
38   var openingQuote;
39
40   var targetIsArray = Array.isArray(target);
41
42   // If the target is just a string, it is easy to check whether
43   // some index of the source matches it.
44   // If the target is an array of strings, though, we have to
45   // check whether some index of the source matches *any* of
46   // those target strings (stopping after the first match).
47   var getMatch = (function () {
48     if (!targetIsArray) {
49       return getMatchBase.bind(null, target);
50     }
51     return function(index) {
52       for (var ti = 0, tl = target.length; ti < tl; ti++) {
53         var checkResult = getMatchBase(target[ti], index);
54         if (checkResult) return checkResult;
55       }
56       return false;
57     }
58   })();
59
60   function getMatchBase(targetString, index) {
61     var targetStringLength = targetString.length;
62
63     // Target is a single character
64     if (targetStringLength === 1 && source[index] !== targetString) return false;
65
66     // Target is multiple characters
67     if (source.substr(index, targetStringLength) !== targetString) return false;
68
69     return {
70       insideParens: insideParens,
71       insideFunctionArguments: insideFunctionArguments,
72       insideComment: insideComment,
73       insideString: insideString,
74       startIndex: index,
75       endIndex: index + targetStringLength,
76       target: targetString,
77     };
78   }
79
80   for (var i = 0, l = source.length; i < l; i++) {
81     var currentChar = source[i];
82
83     // Register the beginning of a comment
84     if (
85       !insideString && !insideComment
86       && currentChar === "/"
87       && source[i - 1] !== "\\" // escaping
88     ) {
89       // standard comments
90       if (source[i + 1] === "*") {
91         insideComment = true;
92         continue;
93       }
94       // single-line comments
95       if (source[i + 1] === "/") {
96         insideComment = true;
97         insideSingleLineComment = true;
98         continue;
99       }
100     }
101
102     if (insideComment) {
103       // Register the end of a standard comment
104       if (
105         !insideSingleLineComment
106         && currentChar === "*"
107         && source[i - 1] !== "\\" // escaping
108         && source[i + 1] === "/"
109         && source[i - 1] !== "/" // don't end if it's /*/
110       ) {
111         insideComment = false;
112         continue;
113       }
114
115       // Register the end of a single-line comment
116       if (
117         insideSingleLineComment
118         && currentChar === "\n"
119       ) {
120         insideComment = false;
121         insideSingleLineComment = false;
122       }
123
124       if (skipComments) continue;
125     }
126
127     // Register the beginning of a string
128     if (!insideComment && !insideString && (currentChar === "\"" || currentChar === "'")) {
129       if (source[i - 1] === "\\") continue; // escaping
130
131       openingQuote = currentChar;
132       insideString = true;
133
134       // For string-quotes rule
135       if (target === currentChar) handleMatch(getMatch(i));
136       continue;
137     }
138
139     if (insideString) {
140       // Register the end of a string
141       if (currentChar === openingQuote) {
142         if (source[i - 1] === "\\") continue; // escaping
143         insideString = false;
144         continue;
145       }
146
147       if (skipStrings) continue;
148     }
149
150     // Register the beginning of parens/functions
151     if (!insideString && !insideComment && currentChar === "(") {
152       // Keep track of opening parentheticals so that we
153       // know when the outermost function (possibly
154       // containing nested functions) is closing
155       openingParenCount++;
156
157       insideParens = true;
158       // Only inside a function if there is a function name
159       // before the opening paren
160       if (/[a-zA-Z]/.test(source[i - 1])) {
161         insideFunctionArguments = true;
162       }
163
164       if (target === "(") handleMatch(getMatch(i));
165       continue;
166     }
167
168     if (insideParens) {
169       // Register the end of a function
170       if (currentChar === ")") {
171         openingParenCount--;
172         // Do this here so the match is still technically inside a function
173         if (target === ")") handleMatch(getMatch(i));
174         if (openingParenCount === 0) {
175           insideParens = false;
176           insideFunctionArguments = false;
177         }
178         continue;
179       }
180     }
181
182     var isFunctionName = /^[a-zA-Z]*\(/.test(source.slice(i));
183     if (skipFunctionNames && isFunctionName) continue;
184     if (onlyFunctionNames && !isFunctionName) continue;
185
186     var match = getMatch(i);
187
188     if (!match) continue;
189     handleMatch(match);
190     if (options.once) return;
191   }
192
193   function handleMatch(match) {
194     if (onlyParentheticals && !insideParens) return;
195     if (skipParentheticals && insideParens) return;
196     if (onlyFunctionArguments && !insideFunctionArguments) return;
197     if (skipFunctionArguments && insideFunctionArguments) return;
198     if (onlyStrings && !insideString) return;
199     if (onlyComments && !insideComment) return;
200     matchCount++;
201     callback(match, matchCount);
202   }
203 }