in src/plugins/console/public/lib/autocomplete/autocomplete.ts [78:321]
export function getCurrentMethodAndTokenPaths(
editor: CoreEditor,
pos: Position,
parser: any,
forceEndOfUrl?: boolean /* Flag for indicating whether we want to avoid early escape optimization. */
) {
const tokenIter = createTokenIterator({
editor,
position: pos,
});
const startPos = pos;
let bodyTokenPath: any = [];
const ret: any = {};
const STATES = {
looking_for_key: 0, // looking for a key but without jumping over anything but white space and colon.
looking_for_scope_start: 1, // skip everything until scope start
start: 3,
};
let state = STATES.start;
// initialization problems -
let t = tokenIter.getCurrentToken();
if (t) {
if (startPos.column === 1) {
// if we are at the beginning of the line, the current token is the one after cursor, not before which
// deviates from the standard.
t = tokenIter.stepBackward();
state = STATES.looking_for_scope_start;
}
} else {
if (startPos.column === 1) {
// empty lines do no have tokens, move one back
t = tokenIter.stepBackward();
state = STATES.start;
}
}
let walkedSomeBody = false;
// climb one scope at a time and get the scope key
for (; t && t.type.indexOf('url') === -1 && t.type !== 'method'; t = tokenIter.stepBackward()) {
if (t.type !== 'whitespace') {
walkedSomeBody = true;
} // marks we saw something
switch (t.type) {
case 'variable':
if (state === STATES.looking_for_key) {
bodyTokenPath.unshift(t.value.trim().replace(/"/g, ''));
}
state = STATES.looking_for_scope_start; // skip everything until the beginning of this scope
break;
case 'paren.lparen':
bodyTokenPath.unshift(t.value);
if (state === STATES.looking_for_scope_start) {
// found it. go look for the relevant key
state = STATES.looking_for_key;
}
break;
case 'paren.rparen':
// reset he search for key
state = STATES.looking_for_scope_start;
// and ignore this sub scope..
let parenCount = 1;
t = tokenIter.stepBackward();
while (t && parenCount > 0) {
switch (t.type) {
case 'paren.lparen':
parenCount--;
break;
case 'paren.rparen':
parenCount++;
break;
}
if (parenCount > 0) {
t = tokenIter.stepBackward();
}
}
if (!t) {
// oops we run out.. we don't know what's up return null;
return {};
}
continue;
case 'punctuation.end_triple_quote':
// reset the search for key
state = STATES.looking_for_scope_start;
for (t = tokenIter.stepBackward(); t; t = tokenIter.stepBackward()) {
if (t.type === 'punctuation.start_triple_quote') {
t = tokenIter.stepBackward();
break;
}
}
if (!t) {
// oops we run out.. we don't know what's up return null;
return {};
}
continue;
case 'punctuation.start_triple_quote':
if (state === STATES.start) {
state = STATES.looking_for_key;
} else if (state === STATES.looking_for_key) {
state = STATES.looking_for_scope_start;
}
bodyTokenPath.unshift('"""');
continue;
case 'string':
case 'constant.numeric':
case 'constant.language.boolean':
case 'text':
if (state === STATES.start) {
state = STATES.looking_for_key;
} else if (state === STATES.looking_for_key) {
state = STATES.looking_for_scope_start;
}
break;
case 'punctuation.comma':
if (state === STATES.start) {
state = STATES.looking_for_scope_start;
}
break;
case 'punctuation.colon':
case 'whitespace':
if (state === STATES.start) {
state = STATES.looking_for_key;
}
break; // skip white space
}
}
if (walkedSomeBody && (!bodyTokenPath || bodyTokenPath.length === 0) && !forceEndOfUrl) {
// we had some content and still no path -> the cursor is position after a closed body -> no auto complete
return {};
}
ret.urlTokenPath = [];
if (tokenIter.getCurrentPosition().lineNumber === startPos.lineNumber) {
if (t && (t.type === 'url.part' || t.type === 'url.param' || t.type === 'url.value')) {
// we are forcing the end of the url for the purposes of determining an endpoint
if (forceEndOfUrl && t.type === 'url.part') {
ret.urlTokenPath.push(t.value);
ret.urlTokenPath.push(URL_PATH_END_MARKER);
}
// we are on the same line as cursor and dealing with a url. Current token is not part of the context
t = tokenIter.stepBackward();
// This will force method parsing
while (t!.type === 'whitespace') {
t = tokenIter.stepBackward();
}
}
bodyTokenPath = null; // no not on a body line.
}
ret.bodyTokenPath = bodyTokenPath;
ret.urlParamsTokenPath = null;
ret.requestStartRow = tokenIter.getCurrentPosition().lineNumber;
let curUrlPart: any;
while (t && isUrlParamsToken(t)) {
switch (t.type) {
case 'url.value':
if (Array.isArray(curUrlPart)) {
curUrlPart.unshift(t.value);
} else if (curUrlPart) {
curUrlPart = [t.value, curUrlPart];
} else {
curUrlPart = t.value;
}
break;
case 'url.comma':
if (!curUrlPart) {
curUrlPart = [];
} else if (!Array.isArray(curUrlPart)) {
curUrlPart = [curUrlPart];
}
break;
case 'url.param':
const v = curUrlPart;
curUrlPart = {};
curUrlPart[t.value] = v;
break;
case 'url.amp':
case 'url.questionmark':
if (!ret.urlParamsTokenPath) {
ret.urlParamsTokenPath = [];
}
ret.urlParamsTokenPath.unshift(curUrlPart || {});
curUrlPart = null;
break;
}
t = tokenIter.stepBackward();
}
curUrlPart = null;
while (t && t.type.indexOf('url') !== -1) {
switch (t.type) {
case 'url.part':
if (Array.isArray(curUrlPart)) {
curUrlPart.unshift(t.value);
} else if (curUrlPart) {
curUrlPart = [t.value, curUrlPart];
} else {
curUrlPart = t.value;
}
break;
case 'url.comma':
if (!curUrlPart) {
curUrlPart = [];
} else if (!Array.isArray(curUrlPart)) {
curUrlPart = [curUrlPart];
}
break;
case 'url.slash':
if (curUrlPart) {
ret.urlTokenPath.unshift(curUrlPart);
curUrlPart = null;
}
break;
}
t = parser.prevNonEmptyToken(tokenIter);
}
if (curUrlPart) {
ret.urlTokenPath.unshift(curUrlPart);
}
if (!ret.bodyTokenPath && !ret.urlParamsTokenPath) {
if (ret.urlTokenPath.length > 0) {
// // started on the url, first token is current token
ret.otherTokenValues = ret.urlTokenPath[0];
}
} else {
// mark the url as completed.
ret.urlTokenPath.push(URL_PATH_END_MARKER);
}
if (t && t.type === 'method') {
ret.method = t.value;
}
return ret;
}