@@ -1,40 +1,40 @@ | |||
! special | |||
*.foreground: #ffffff | |||
*.background: #1b1b1b | |||
*.background: #000000 | |||
*.cursorColor: #ffffff | |||
! black | |||
*.color0: #3d3d3d | |||
*.color8: #4d4d4d | |||
*.color0: #2e3436 | |||
*.color8: #555753 | |||
! red | |||
*.color1: #6673bf | |||
*.color9: #899aff | |||
*.color1: #a31604 | |||
*.color9: #c60001 | |||
! green | |||
*.color2: #3ea290 | |||
*.color10: #52ad91 | |||
*.color2: #447241 | |||
*.color10: #27a343 | |||
! yellow | |||
*.color3: #b0ead9 | |||
*.color11: #98c9bb | |||
*.color3: #c1951a | |||
*.color11: #d5a30e | |||
! blue | |||
*.color4: #31658c | |||
*.color12: #477ab3 | |||
*.color4: #41008c | |||
*.color12: #531699 | |||
! magenta | |||
*.color5: #596196 | |||
*.color13: #7882bf | |||
*.color5: #965d98 | |||
*.color13: #893c8c | |||
! cyan | |||
*.color6: #8292b2 | |||
*.color14: #95a7cc | |||
*.color6: #1276a7 | |||
*.color14: #3391bf | |||
! white | |||
*.color7: #c8cacc | |||
*.color15: #edeff2 | |||
*.color7: #d3d7cf | |||
*.color15: #eeeeec | |||
urxvt.shading: 0 | |||
urxvt.transparent: false | |||
@@ -1 +0,0 @@ | |||
{} |
@@ -1 +0,0 @@ | |||
server |
@@ -1,29 +0,0 @@ | |||
# coc-emmet | |||
Emmet completion support for [coc.nvim](https://github.com/neoclide/coc.nvim) | |||
Fork of emmet extension from [VSCode](https://github.com/Microsoft/vscode) with | |||
only completion support. | |||
## Install | |||
In your vim/neovim, run command: | |||
```vim | |||
:CocInstall coc-emmet | |||
``` | |||
## Options | |||
- `emmet.showExpandedAbbreviation`: Shows expanded Emmet abbreviations as suggestions, default `true`. | |||
- `emmet.showAbbreviationSuggestions`: Shows possible Emmet abbreviations as suggestions. Not applicable in stylesheets or when emmet.showExpandedAbbreviation is 'never'. | |||
- `emmet.includeLanguages`: Enable Emmet abbreviations in languages that are not supported by default. Add a mapping here between the language and emmet supported language. E.g.: `{"vue-html": "html", "javascript": "javascriptreact"}` | |||
- `emmet.variables`: Variables to be used in Emmet snippets | |||
- `emmet.syntaxProfiles`: Define profile for specified syntax or use your own profile with specific rules. | |||
- `emmet.excludeLanguages`: An array of languages where Emmet abbreviations should not be expanded, default: `["markdown"]`. | |||
- `emmet.optimizeStylesheetParsing`: When set to `false`, the whole file is parsed to determine if current position is valid for expanding Emmet abbreviations. When set to `true`, only the content around the current position in css/scss/less files is parsed. | |||
- `emmet.preferences`: Preferences used to modify behavior of some actions and resolvers of Emmet. | |||
## LICENSE | |||
MIT |
@@ -1,21 +0,0 @@ | |||
MIT License | |||
Copyright (c) 2017 Emmet.io | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. |
@@ -1,2 +0,0 @@ | |||
# extract-abbreviation | |||
Extracts Emmet abbreviation from string |
@@ -1,373 +0,0 @@ | |||
'use strict'; | |||
/** | |||
* Minimalistic backwards stream reader | |||
*/ | |||
class StreamReader { | |||
constructor(string) { | |||
this.string = string; | |||
this.pos = this.string.length; | |||
} | |||
sol() { | |||
return this.pos === 0; | |||
} | |||
peek(offset) { | |||
return this.string.charCodeAt(this.pos - 1 + (offset || 0)); | |||
} | |||
prev() { | |||
if (!this.sol()) { | |||
return this.string.charCodeAt(--this.pos); | |||
} | |||
} | |||
eat(match) { | |||
const ok = typeof match === 'function' | |||
? match(this.peek()) | |||
: match === this.peek(); | |||
if (ok) { | |||
this.pos--; | |||
} | |||
return ok; | |||
} | |||
eatWhile(match) { | |||
const start = this.pos; | |||
while (this.eat(match)) {} | |||
return this.pos < start; | |||
} | |||
} | |||
/** | |||
* Quotes-related utilities | |||
*/ | |||
const SINGLE_QUOTE = 39; // ' | |||
const DOUBLE_QUOTE = 34; // " | |||
const ESCAPE = 92; // \ | |||
/** | |||
* Check if given character code is a quote | |||
* @param {Number} c | |||
* @return {Boolean} | |||
*/ | |||
function isQuote(c) { | |||
return c === SINGLE_QUOTE || c === DOUBLE_QUOTE; | |||
} | |||
/** | |||
* Consumes quoted value, if possible | |||
* @param {StreamReader} stream | |||
* @return {Boolean} Returns `true` is value was consumed | |||
*/ | |||
function eatQuoted(stream) { | |||
const start = stream.pos; | |||
const quote = stream.prev(); | |||
if (isQuote(quote)) { | |||
while (!stream.sol()) { | |||
if (stream.prev() === quote && stream.peek() !== ESCAPE) { | |||
return true; | |||
} | |||
} | |||
} | |||
stream.pos = start; | |||
return false; | |||
} | |||
const TAB = 9; | |||
const SPACE = 32; | |||
const DASH = 45; // - | |||
const SLASH = 47; // / | |||
const COLON = 58; // : | |||
const EQUALS = 61; // = | |||
const ANGLE_LEFT = 60; // < | |||
const ANGLE_RIGHT = 62; // > | |||
/** | |||
* Check if given reader’s current position points at the end of HTML tag | |||
* @param {StreamReader} stream | |||
* @return {Boolean} | |||
*/ | |||
var isAtHTMLTag = function (stream) { | |||
const start = stream.pos; | |||
if (!stream.eat(ANGLE_RIGHT)) { | |||
return false; | |||
} | |||
let ok = false; | |||
stream.eat(SLASH); // possibly self-closed element | |||
while (!stream.sol()) { | |||
stream.eatWhile(isWhiteSpace); | |||
if (eatIdent(stream)) { | |||
// ate identifier: could be a tag name, boolean attribute or unquoted | |||
// attribute value | |||
if (stream.eat(SLASH)) { | |||
// either closing tag or invalid tag | |||
ok = stream.eat(ANGLE_LEFT); | |||
break; | |||
} else if (stream.eat(ANGLE_LEFT)) { | |||
// opening tag | |||
ok = true; | |||
break; | |||
} else if (stream.eat(isWhiteSpace)) { | |||
// boolean attribute | |||
continue; | |||
} else if (stream.eat(EQUALS)) { | |||
// simple unquoted value or invalid attribute | |||
if (eatIdent(stream)) { | |||
continue; | |||
} | |||
break; | |||
} else if (eatAttributeWithUnquotedValue(stream)) { | |||
// identifier was a part of unquoted value | |||
ok = true; | |||
break; | |||
} | |||
// invalid tag | |||
break; | |||
} | |||
if (eatAttribute(stream)) { | |||
continue; | |||
} | |||
break; | |||
} | |||
stream.pos = start; | |||
return ok; | |||
}; | |||
/** | |||
* Eats HTML attribute from given string. | |||
* @param {StreamReader} state | |||
* @return {Boolean} `true` if attribute was consumed. | |||
*/ | |||
function eatAttribute(stream) { | |||
return eatAttributeWithQuotedValue(stream) || eatAttributeWithUnquotedValue(stream); | |||
} | |||
/** | |||
* @param {StreamReader} stream | |||
* @return {Boolean} | |||
*/ | |||
function eatAttributeWithQuotedValue(stream) { | |||
const start = stream.pos; | |||
if (eatQuoted(stream) && stream.eat(EQUALS) && eatIdent(stream)) { | |||
return true; | |||
} | |||
stream.pos = start; | |||
return false; | |||
} | |||
/** | |||
* @param {StreamReader} stream | |||
* @return {Boolean} | |||
*/ | |||
function eatAttributeWithUnquotedValue(stream) { | |||
const start = stream.pos; | |||
if (stream.eatWhile(isUnquotedValue) && stream.eat(EQUALS) && eatIdent(stream)) { | |||
return true; | |||
} | |||
stream.pos = start; | |||
return false; | |||
} | |||
/** | |||
* Eats HTML identifier from stream | |||
* @param {StreamReader} stream | |||
* @return {Boolean} | |||
*/ | |||
function eatIdent(stream) { | |||
return stream.eatWhile(isIdent); | |||
} | |||
/** | |||
* Check if given character code belongs to HTML identifier | |||
* @param {Number} c | |||
* @return {Boolean} | |||
*/ | |||
function isIdent(c) { | |||
return c === COLON || c === DASH || isAlpha(c) || isNumber(c); | |||
} | |||
/** | |||
* Check if given character code is alpha code (letter though A to Z) | |||
* @param {Number} c | |||
* @return {Boolean} | |||
*/ | |||
function isAlpha(c) { | |||
c &= ~32; // quick hack to convert any char code to uppercase char code | |||
return c >= 65 && c <= 90; // A-Z | |||
} | |||
/** | |||
* Check if given code is a number | |||
* @param {Number} c | |||
* @return {Boolean} | |||
*/ | |||
function isNumber(c) { | |||
return c > 47 && c < 58; | |||
} | |||
/** | |||
* Check if given code is a whitespace | |||
* @param {Number} c | |||
* @return {Boolean} | |||
*/ | |||
function isWhiteSpace(c) { | |||
return c === SPACE || c === TAB; | |||
} | |||
/** | |||
* Check if given code may belong to unquoted attribute value | |||
* @param {Number} c | |||
* @return {Boolean} | |||
*/ | |||
function isUnquotedValue(c) { | |||
return c && c !== EQUALS && !isWhiteSpace(c) && !isQuote(c); | |||
} | |||
const code = ch => ch.charCodeAt(0); | |||
const SQUARE_BRACE_L = code('['); | |||
const SQUARE_BRACE_R = code(']'); | |||
const ROUND_BRACE_L = code('('); | |||
const ROUND_BRACE_R = code(')'); | |||
const CURLY_BRACE_L = code('{'); | |||
const CURLY_BRACE_R = code('}'); | |||
const specialChars = new Set('#.*:$-_!@%^+>/'.split('').map(code)); | |||
const bracePairs = new Map() | |||
.set(SQUARE_BRACE_L, SQUARE_BRACE_R) | |||
.set(ROUND_BRACE_L, ROUND_BRACE_R) | |||
.set(CURLY_BRACE_L, CURLY_BRACE_R); | |||
const defaultOptions = { | |||
syntax: 'markup', | |||
lookAhead: null | |||
}; | |||
/** | |||
* Extracts Emmet abbreviation from given string. | |||
* The goal of this module is to extract abbreviation from current editor’s line, | |||
* e.g. like this: `<span>.foo[title=bar|]</span>` -> `.foo[title=bar]`, where | |||
* `|` is a current caret position. | |||
* @param {String} line A text line where abbreviation should be expanded | |||
* @param {Number} [pos] Caret position in line. If not given, uses end-of-line | |||
* @param {Object} [options] | |||
* @param {Boolean} [options.lookAhead] Allow parser to look ahead of `pos` index for | |||
* searching of missing abbreviation parts. Most editors automatically inserts | |||
* closing braces for `[`, `{` and `(`, which will most likely be right after | |||
* current caret position. So in order to properly expand abbreviation, user | |||
* must explicitly move caret right after auto-inserted braces. Whith this option | |||
* enabled, parser will search for closing braces right after `pos`. Default is `true` | |||
* @param {String} [options.syntax] Name of context syntax of expanded abbreviation. | |||
* Either 'markup' (default) or 'stylesheet'. In 'stylesheet' syntax, braces `[]` | |||
* and `{}` are not supported thus not extracted. | |||
* @return {Object} Object with `abbreviation` and its `location` in given line | |||
* if abbreviation can be extracted, `null` otherwise | |||
*/ | |||
function extractAbbreviation(line, pos, options) { | |||
// make sure `pos` is within line range | |||
pos = Math.min(line.length, Math.max(0, pos == null ? line.length : pos)); | |||
if (typeof options === 'boolean') { | |||
options = Object.assign(defaultOptions, { lookAhead: options }); | |||
} else { | |||
options = Object.assign(defaultOptions, options); | |||
} | |||
if (options.lookAhead == null || options.lookAhead === true) { | |||
pos = offsetPastAutoClosed(line, pos, options); | |||
} | |||
let c; | |||
const stream = new StreamReader(line); | |||
stream.pos = pos; | |||
const stack = []; | |||
while (!stream.sol()) { | |||
c = stream.peek(); | |||
if (isCloseBrace(c, options.syntax)) { | |||
stack.push(c); | |||
} else if (isOpenBrace(c, options.syntax)) { | |||
if (stack.pop() !== bracePairs.get(c)) { | |||
// unexpected brace | |||
break; | |||
} | |||
} else if (has(stack, SQUARE_BRACE_R) || has(stack, CURLY_BRACE_R)) { | |||
// respect all characters inside attribute sets or text nodes | |||
stream.pos--; | |||
continue; | |||
} else if (isAtHTMLTag(stream) || !isAbbreviation(c)) { | |||
break; | |||
} | |||
stream.pos--; | |||
} | |||
if (!stack.length && stream.pos !== pos) { | |||
// found something, remove some invalid symbols from the | |||
// beginning and return abbreviation | |||
const abbreviation = line.slice(stream.pos, pos).replace(/^[*+>^]+/, ''); | |||
return { | |||
abbreviation, | |||
location: pos - abbreviation.length | |||
}; | |||
} | |||
} | |||
/** | |||
* Returns new `line` index which is right after characters beyound `pos` that | |||
* edditor will likely automatically close, e.g. }, ], and quotes | |||
* @param {String} line | |||
* @param {Number} pos | |||
* @return {Number} | |||
*/ | |||
function offsetPastAutoClosed(line, pos, options) { | |||
// closing quote is allowed only as a next character | |||
if (isQuote(line.charCodeAt(pos))) { | |||
pos++; | |||
} | |||
// offset pointer until non-autoclosed character is found | |||
while (isCloseBrace(line.charCodeAt(pos), options.syntax)) { | |||
pos++; | |||
} | |||
return pos; | |||
} | |||
function has(arr, value) { | |||
return arr.indexOf(value) !== -1; | |||
} | |||
function isAbbreviation(c) { | |||
return (c > 64 && c < 91) // uppercase letter | |||
|| (c > 96 && c < 123) // lowercase letter | |||
|| (c > 47 && c < 58) // number | |||
|| specialChars.has(c); // special character | |||
} | |||
function isOpenBrace(c, syntax) { | |||
return c === ROUND_BRACE_L || (syntax === 'markup' && (c === SQUARE_BRACE_L || c === CURLY_BRACE_L)); | |||
} | |||
function isCloseBrace(c, syntax) { | |||
return c === ROUND_BRACE_R || (syntax === 'markup' && (c === SQUARE_BRACE_R || c === CURLY_BRACE_R)); | |||
} | |||
module.exports = extractAbbreviation; |
@@ -1,371 +0,0 @@ | |||
/** | |||
* Minimalistic backwards stream reader | |||
*/ | |||
class StreamReader { | |||
constructor(string) { | |||
this.string = string; | |||
this.pos = this.string.length; | |||
} | |||
sol() { | |||
return this.pos === 0; | |||
} | |||
peek(offset) { | |||
return this.string.charCodeAt(this.pos - 1 + (offset || 0)); | |||
} | |||
prev() { | |||
if (!this.sol()) { | |||
return this.string.charCodeAt(--this.pos); | |||
} | |||
} | |||
eat(match) { | |||
const ok = typeof match === 'function' | |||
? match(this.peek()) | |||
: match === this.peek(); | |||
if (ok) { | |||
this.pos--; | |||
} | |||
return ok; | |||
} | |||
eatWhile(match) { | |||
const start = this.pos; | |||
while (this.eat(match)) {} | |||
return this.pos < start; | |||
} | |||
} | |||
/** | |||
* Quotes-related utilities | |||
*/ | |||
const SINGLE_QUOTE = 39; // ' | |||
const DOUBLE_QUOTE = 34; // " | |||
const ESCAPE = 92; // \ | |||
/** | |||
* Check if given character code is a quote | |||
* @param {Number} c | |||
* @return {Boolean} | |||
*/ | |||
function isQuote(c) { | |||
return c === SINGLE_QUOTE || c === DOUBLE_QUOTE; | |||
} | |||
/** | |||
* Consumes quoted value, if possible | |||
* @param {StreamReader} stream | |||
* @return {Boolean} Returns `true` is value was consumed | |||
*/ | |||
function eatQuoted(stream) { | |||
const start = stream.pos; | |||
const quote = stream.prev(); | |||
if (isQuote(quote)) { | |||
while (!stream.sol()) { | |||
if (stream.prev() === quote && stream.peek() !== ESCAPE) { | |||
return true; | |||
} | |||
} | |||
} | |||
stream.pos = start; | |||
return false; | |||
} | |||
const TAB = 9; | |||
const SPACE = 32; | |||
const DASH = 45; // - | |||
const SLASH = 47; // / | |||
const COLON = 58; // : | |||
const EQUALS = 61; // = | |||
const ANGLE_LEFT = 60; // < | |||
const ANGLE_RIGHT = 62; // > | |||
/** | |||
* Check if given reader’s current position points at the end of HTML tag | |||
* @param {StreamReader} stream | |||
* @return {Boolean} | |||
*/ | |||
var isAtHTMLTag = function (stream) { | |||
const start = stream.pos; | |||
if (!stream.eat(ANGLE_RIGHT)) { | |||
return false; | |||
} | |||
let ok = false; | |||
stream.eat(SLASH); // possibly self-closed element | |||
while (!stream.sol()) { | |||
stream.eatWhile(isWhiteSpace); | |||
if (eatIdent(stream)) { | |||
// ate identifier: could be a tag name, boolean attribute or unquoted | |||
// attribute value | |||
if (stream.eat(SLASH)) { | |||
// either closing tag or invalid tag | |||
ok = stream.eat(ANGLE_LEFT); | |||
break; | |||
} else if (stream.eat(ANGLE_LEFT)) { | |||
// opening tag | |||
ok = true; | |||
break; | |||
} else if (stream.eat(isWhiteSpace)) { | |||
// boolean attribute | |||
continue; | |||
} else if (stream.eat(EQUALS)) { | |||
// simple unquoted value or invalid attribute | |||
if (eatIdent(stream)) { | |||
continue; | |||
} | |||
break; | |||
} else if (eatAttributeWithUnquotedValue(stream)) { | |||
// identifier was a part of unquoted value | |||
ok = true; | |||
break; | |||
} | |||
// invalid tag | |||
break; | |||
} | |||
if (eatAttribute(stream)) { | |||
continue; | |||
} | |||
break; | |||
} | |||
stream.pos = start; | |||
return ok; | |||
}; | |||
/** | |||
* Eats HTML attribute from given string. | |||
* @param {StreamReader} state | |||
* @return {Boolean} `true` if attribute was consumed. | |||
*/ | |||
function eatAttribute(stream) { | |||
return eatAttributeWithQuotedValue(stream) || eatAttributeWithUnquotedValue(stream); | |||
} | |||
/** | |||
* @param {StreamReader} stream | |||
* @return {Boolean} | |||
*/ | |||
function eatAttributeWithQuotedValue(stream) { | |||
const start = stream.pos; | |||
if (eatQuoted(stream) && stream.eat(EQUALS) && eatIdent(stream)) { | |||
return true; | |||
} | |||
stream.pos = start; | |||
return false; | |||
} | |||
/** | |||
* @param {StreamReader} stream | |||
* @return {Boolean} | |||
*/ | |||
function eatAttributeWithUnquotedValue(stream) { | |||
const start = stream.pos; | |||
if (stream.eatWhile(isUnquotedValue) && stream.eat(EQUALS) && eatIdent(stream)) { | |||
return true; | |||
} | |||
stream.pos = start; | |||
return false; | |||
} | |||
/** | |||
* Eats HTML identifier from stream | |||
* @param {StreamReader} stream | |||
* @return {Boolean} | |||
*/ | |||
function eatIdent(stream) { | |||
return stream.eatWhile(isIdent); | |||
} | |||
/** | |||
* Check if given character code belongs to HTML identifier | |||
* @param {Number} c | |||
* @return {Boolean} | |||
*/ | |||
function isIdent(c) { | |||
return c === COLON || c === DASH || isAlpha(c) || isNumber(c); | |||
} | |||
/** | |||
* Check if given character code is alpha code (letter though A to Z) | |||
* @param {Number} c | |||
* @return {Boolean} | |||
*/ | |||
function isAlpha(c) { | |||
c &= ~32; // quick hack to convert any char code to uppercase char code | |||
return c >= 65 && c <= 90; // A-Z | |||
} | |||
/** | |||
* Check if given code is a number | |||
* @param {Number} c | |||
* @return {Boolean} | |||
*/ | |||
function isNumber(c) { | |||
return c > 47 && c < 58; | |||
} | |||
/** | |||
* Check if given code is a whitespace | |||
* @param {Number} c | |||
* @return {Boolean} | |||
*/ | |||
function isWhiteSpace(c) { | |||
return c === SPACE || c === TAB; | |||
} | |||
/** | |||
* Check if given code may belong to unquoted attribute value | |||
* @param {Number} c | |||
* @return {Boolean} | |||
*/ | |||
function isUnquotedValue(c) { | |||
return c && c !== EQUALS && !isWhiteSpace(c) && !isQuote(c); | |||
} | |||
const code = ch => ch.charCodeAt(0); | |||
const SQUARE_BRACE_L = code('['); | |||
const SQUARE_BRACE_R = code(']'); | |||
const ROUND_BRACE_L = code('('); | |||
const ROUND_BRACE_R = code(')'); | |||
const CURLY_BRACE_L = code('{'); | |||
const CURLY_BRACE_R = code('}'); | |||
const specialChars = new Set('#.*:$-_!@%^+>/'.split('').map(code)); | |||
const bracePairs = new Map() | |||
.set(SQUARE_BRACE_L, SQUARE_BRACE_R) | |||
.set(ROUND_BRACE_L, ROUND_BRACE_R) | |||
.set(CURLY_BRACE_L, CURLY_BRACE_R); | |||
const defaultOptions = { | |||
syntax: 'markup', | |||
lookAhead: null | |||
}; | |||
/** | |||
* Extracts Emmet abbreviation from given string. | |||
* The goal of this module is to extract abbreviation from current editor’s line, | |||
* e.g. like this: `<span>.foo[title=bar|]</span>` -> `.foo[title=bar]`, where | |||
* `|` is a current caret position. | |||
* @param {String} line A text line where abbreviation should be expanded | |||
* @param {Number} [pos] Caret position in line. If not given, uses end-of-line | |||
* @param {Object} [options] | |||
* @param {Boolean} [options.lookAhead] Allow parser to look ahead of `pos` index for | |||
* searching of missing abbreviation parts. Most editors automatically inserts | |||
* closing braces for `[`, `{` and `(`, which will most likely be right after | |||
* current caret position. So in order to properly expand abbreviation, user | |||
* must explicitly move caret right after auto-inserted braces. Whith this option | |||
* enabled, parser will search for closing braces right after `pos`. Default is `true` | |||
* @param {String} [options.syntax] Name of context syntax of expanded abbreviation. | |||
* Either 'markup' (default) or 'stylesheet'. In 'stylesheet' syntax, braces `[]` | |||
* and `{}` are not supported thus not extracted. | |||
* @return {Object} Object with `abbreviation` and its `location` in given line | |||
* if abbreviation can be extracted, `null` otherwise | |||
*/ | |||
function extractAbbreviation(line, pos, options) { | |||
// make sure `pos` is within line range | |||
pos = Math.min(line.length, Math.max(0, pos == null ? line.length : pos)); | |||
if (typeof options === 'boolean') { | |||
options = Object.assign(defaultOptions, { lookAhead: options }); | |||
} else { | |||
options = Object.assign(defaultOptions, options); | |||
} | |||
if (options.lookAhead == null || options.lookAhead === true) { | |||
pos = offsetPastAutoClosed(line, pos, options); | |||
} | |||
let c; | |||
const stream = new StreamReader(line); | |||
stream.pos = pos; | |||
const stack = []; | |||
while (!stream.sol()) { | |||
c = stream.peek(); | |||
if (isCloseBrace(c, options.syntax)) { | |||
stack.push(c); | |||
} else if (isOpenBrace(c, options.syntax)) { | |||
if (stack.pop() !== bracePairs.get(c)) { | |||
// unexpected brace | |||
break; | |||
} | |||
} else if (has(stack, SQUARE_BRACE_R) || has(stack, CURLY_BRACE_R)) { | |||
// respect all characters inside attribute sets or text nodes | |||
stream.pos--; | |||
continue; | |||
} else if (isAtHTMLTag(stream) || !isAbbreviation(c)) { | |||
break; | |||
} | |||
stream.pos--; | |||
} | |||
if (!stack.length && stream.pos !== pos) { | |||
// found something, remove some invalid symbols from the | |||
// beginning and return abbreviation | |||
const abbreviation = line.slice(stream.pos, pos).replace(/^[*+>^]+/, ''); | |||
return { | |||
abbreviation, | |||
location: pos - abbreviation.length | |||
}; | |||
} | |||
} | |||
/** | |||
* Returns new `line` index which is right after characters beyound `pos` that | |||
* edditor will likely automatically close, e.g. }, ], and quotes | |||
* @param {String} line | |||
* @param {Number} pos | |||
* @return {Number} | |||
*/ | |||
function offsetPastAutoClosed(line, pos, options) { | |||
// closing quote is allowed only as a next character | |||
if (isQuote(line.charCodeAt(pos))) { | |||
pos++; | |||
} | |||
// offset pointer until non-autoclosed character is found | |||
while (isCloseBrace(line.charCodeAt(pos), options.syntax)) { | |||
pos++; | |||
} | |||
return pos; | |||
} | |||
function has(arr, value) { | |||
return arr.indexOf(value) !== -1; | |||
} | |||
function isAbbreviation(c) { | |||
return (c > 64 && c < 91) // uppercase letter | |||
|| (c > 96 && c < 123) // lowercase letter | |||
|| (c > 47 && c < 58) // number | |||
|| specialChars.has(c); // special character | |||
} | |||
function isOpenBrace(c, syntax) { | |||
return c === ROUND_BRACE_L || (syntax === 'markup' && (c === SQUARE_BRACE_L || c === CURLY_BRACE_L)); | |||
} | |||
function isCloseBrace(c, syntax) { | |||
return c === ROUND_BRACE_R || (syntax === 'markup' && (c === SQUARE_BRACE_R || c === CURLY_BRACE_R)); | |||
} | |||
export default extractAbbreviation; |
@@ -1,58 +0,0 @@ | |||
{ | |||
"_from": "@emmetio/extract-abbreviation@0.1.6", | |||
"_id": "@emmetio/extract-abbreviation@0.1.6", | |||
"_inBundle": false, | |||
"_integrity": "sha512-Ce3xE2JvTSEbASFbRbA1gAIcMcZWdS2yUYRaQbeM0nbOzaZrUYfa3ePtcriYRZOZmr+CkKA+zbjhvTpIOAYVcw==", | |||
"_location": "/@emmetio/extract-abbreviation", | |||
"_phantomChildren": {}, | |||
"_requested": { | |||
"type": "version", | |||
"registry": true, | |||
"raw": "@emmetio/extract-abbreviation@0.1.6", | |||
"name": "@emmetio/extract-abbreviation", | |||
"escapedName": "@emmetio%2fextract-abbreviation", | |||
"scope": "@emmetio", | |||
"rawSpec": "0.1.6", | |||
"saveSpec": null, | |||
"fetchSpec": "0.1.6" | |||
}, | |||
"_requiredBy": [ | |||
"/vscode-emmet-helper" | |||
], | |||
"_resolved": "https://registry.npmjs.org/@emmetio/extract-abbreviation/-/extract-abbreviation-0.1.6.tgz", | |||
"_shasum": "e4a9856c1057f0aff7d443b8536477c243abe28c", | |||
"_spec": "@emmetio/extract-abbreviation@0.1.6", | |||
"_where": "/tmp/coc-emmet-I4j5XV/node_modules/vscode-emmet-helper", | |||
"author": { | |||
"name": "Sergey Chikuyonok", | |||
"email": "serge.che@gmail.com" | |||
}, | |||
"bugs": { | |||
"url": "https://github.com/emmetio/extract-abbreviation/issues" | |||
}, | |||
"bundleDependencies": false, | |||
"deprecated": false, | |||
"description": "Extracts Emmet abbreviation from string", | |||
"devDependencies": { | |||
"babel-plugin-transform-es2015-modules-commonjs": "^6.22.0", | |||
"babel-register": "^6.22.0", | |||
"mocha": "^3.2.0", | |||
"rollup": "^0.41.4" | |||
}, | |||
"homepage": "https://github.com/emmetio/extract-abbreviation#readme", | |||
"keywords": [], | |||
"license": "MIT", | |||
"main": "dist/extract-abbreviation.cjs.js", | |||
"module": "dist/extract-abbreviation.es.js", | |||
"name": "@emmetio/extract-abbreviation", | |||
"repository": { | |||
"type": "git", | |||
"url": "git+https://github.com/emmetio/extract-abbreviation.git" | |||
}, | |||
"scripts": { | |||
"build": "rollup -c", | |||
"prepublish": "npm test && npm run build", | |||
"test": "mocha" | |||
}, | |||
"version": "0.1.6" | |||
} |
@@ -1,4 +0,0 @@ | |||
{ | |||
perform: true, | |||
assignees: [ aeschli ] | |||
} |
@@ -1,5 +0,0 @@ | |||
language: node_js | |||
sudo: false | |||
node_js: | |||
- "6" |
@@ -1,35 +0,0 @@ | |||
1.0.3 2018-03-07 | |||
================== | |||
- provide ems modules | |||
1.0.2 2018-03-05 | |||
================== | |||
- added the *visit.onComment* API, reported when comments are allowed. | |||
- added the *ParseErrorCode.InvalidCommentToken* enum value, reported when comments are disallowed. | |||
1.0.1 | |||
================== | |||
- added the *format* API: computes edits to format a JSON document. | |||
- added the *modify* API: computes edits to insert, remove or replace a property or value in a JSON document. | |||
- added the *allyEdits* API: applies edits to a document | |||
1.0.0 | |||
================== | |||
* remove nls dependency (remove getParseErrorMessage) | |||
0.4.2 / 2017-05-05 | |||
================== | |||
* added ParseError.offset & ParseError.length | |||
0.4.1 / 2017-04-02 | |||
================== | |||
* added ParseOptions.allowTrailingComma | |||
0.4.0 / 2017-02-23 | |||
================== | |||
* fix for `getLocation`. Now `getLocation` inside an object will always return a property from inside that property. Can be empty string if the object has no properties or if the offset is before a actual property `{ "a": { | }} will return location ['a', ' ']` | |||
0.3.0 / 2017-01-17 | |||
================== | |||
* Updating to typescript 2.0 |
@@ -1,21 +0,0 @@ | |||
The MIT License (MIT) | |||
Copyright (c) Microsoft | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. |
@@ -1,291 +0,0 @@ | |||
# jsonc-parser | |||
Scanner and parser for JSON with comments. | |||
[![npm Package](https://img.shields.io/npm/v/jsonc-parser.svg?style=flat-square)](https://www.npmjs.org/package/jsonc-parser) | |||
[![NPM Downloads](https://img.shields.io/npm/dm/jsonc-parser.svg)](https://npmjs.org/package/jsonc-parser) | |||
[![Build Status](https://travis-ci.org/Microsoft/node-jsonc-parser.svg?branch=master)](https://travis-ci.org/Microsoft/node-jsonc-parser) | |||
Why? | |||
---- | |||
JSONC is JSON with JavaScript style comments. This node module provides a scanner and fault tolerant parser that can process JSONC but is also useful for standard JSON. | |||
- the *scanner* tokenizes the input string into tokens and token offsets | |||
- the *visit* function implements a 'SAX' style parser with callbacks for the encountered properties and values. | |||
- the *parseTree* function computes a hierarchical DOM with offsets representing the encountered properties and values. | |||
- the *parse* function evaluates the JavaScipt object represented by JSON string in a fault tolerant fashion. | |||
- the *getLocation* API returns a location object that describes the property or value located at a given offset in a JSON document. | |||
- ths *findNodeAtLocation* API finds the node at a given location path in a JSON DOM. | |||
- the *format* API computes edits to format a JSON document. | |||
- the *modify* API computes edits to insert, remove or replace a property or value in a JSON document. | |||
- the *applyEdits* API applies edits to a document. | |||
Installation | |||
------------ | |||
npm install --save jsonc-parser | |||
API | |||
--- | |||
### Scanner: | |||
```typescript | |||
/** | |||
* Creates a JSON scanner on the given text. | |||
* If ignoreTrivia is set, whitespaces or comments are ignored. | |||
*/ | |||
export function createScanner(text:string, ignoreTrivia:boolean = false):JSONScanner; | |||
/** | |||
* The scanner object, representing a JSON scanner at a position in the input string. | |||
*/ | |||
export interface JSONScanner { | |||
/** | |||
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token. | |||
*/ | |||
setPosition(pos: number): any; | |||
/** | |||
* Read the next token. Returns the tolen code. | |||
*/ | |||
scan(): SyntaxKind; | |||
/** | |||
* Returns the current scan position, which is after the last read token. | |||
*/ | |||
getPosition(): number; | |||
/** | |||
* Returns the last read token. | |||
*/ | |||
getToken(): SyntaxKind; | |||
/** | |||
* Returns the last read token value. The value for strings is the decoded string content. For numbers its of type number, for boolean it's true or false. | |||
*/ | |||
getTokenValue(): string; | |||
/** | |||
* The start offset of the last read token. | |||
*/ | |||
getTokenOffset(): number; | |||
/** | |||
* The length of the last read token. | |||
*/ | |||
getTokenLength(): number; | |||
/** | |||
* An error code of the last scan. | |||
*/ | |||
getTokenError(): ScanError; | |||
} | |||
``` | |||
### Parser: | |||
```typescript | |||
export interface ParseOptions { | |||
disallowComments?: boolean; | |||
} | |||
/** | |||
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault lolerant as possible, but still return a result. | |||
* Therefore always check the errors list to find out if the input was valid. | |||
*/ | |||
export declare function parse(text: string, errors?: {error: ParseErrorCode;}[], options?: ParseOptions): any; | |||
/** | |||
* Parses the given text and invokes the visitor functions for each object, array and literal reached. | |||
*/ | |||
export declare function visit(text: string, visitor: JSONVisitor, options?: ParseOptions): any; | |||
export interface JSONVisitor { | |||
/** | |||
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace. | |||
*/ | |||
onObjectBegin?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked when a property is encountered. The offset and length represent the location of the property name. | |||
*/ | |||
onObjectProperty?: (property: string, offset: number, length: number) => void; | |||
/** | |||
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace. | |||
*/ | |||
onObjectEnd?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket. | |||
*/ | |||
onArrayBegin?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket. | |||
*/ | |||
onArrayEnd?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value. | |||
*/ | |||
onLiteralValue?: (value: any, offset: number, length: number) => void; | |||
/** | |||
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator. | |||
*/ | |||
onSeparator?: (charcter: string, offset: number, length: number) => void; | |||
/** | |||
* When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment. | |||
*/ | |||
onComment?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked on an error. | |||
*/ | |||
onError?: (error: ParseErrorCode, offset: number, length: number) => void; | |||
} | |||
/** | |||
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. | |||
*/ | |||
export declare function parseTree(text: string, errors?: ParseError[], options?: ParseOptions): Node; | |||
export declare type NodeType = "object" | "array" | "property" | "string" | "number" | "boolean" | "null"; | |||
export interface Node { | |||
type: NodeType; | |||
value?: any; | |||
offset: number; | |||
length: number; | |||
columnOffset?: number; | |||
parent?: Node; | |||
children?: Node[]; | |||
} | |||
``` | |||
### Utilities: | |||
```typescript | |||
/** | |||
* Takes JSON with JavaScript-style comments and remove | |||
* them. Optionally replaces every none-newline character | |||
* of comments with a replaceCharacter | |||
*/ | |||
export declare function stripComments(text: string, replaceCh?: string): string; | |||
/** | |||
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index. | |||
*/ | |||
export declare function getLocation(text: string, position: number): Location; | |||
export declare type Segment = string | number; | |||
export interface Location { | |||
/** | |||
* The previous property key or literal value (string, number, boolean or null) or undefined. | |||
*/ | |||
previousNode?: Node; | |||
/** | |||
* The path describing the location in the JSON document. The path consists of a sequence strings | |||
* representing an object property or numbers for array indices. | |||
*/ | |||
path: Segment[]; | |||
/** | |||
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices). | |||
* '*' will match a single segment, of any property name or index. | |||
* '**' will match a sequece of segments or no segment, of any property name or index. | |||
*/ | |||
matches: (patterns: Segment[]) => boolean; | |||
/** | |||
* If set, the location's offset is at a property key. | |||
*/ | |||
isAtPropertyKey: boolean; | |||
} | |||
/** | |||
* Finds the node at the given path in a JSON DOM. | |||
*/ | |||
export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined; | |||
/** | |||
* Evaluates the JavaScript object of the given JSON DOM node | |||
*/ | |||
export function getNodeValue(node: Node): any; | |||
/** | |||
* Computes the edits needed to format a JSON document. | |||
* | |||
* @param documentText The input text | |||
* @param range The range to format or `undefined` to format the full content | |||
* @param options The formatting options | |||
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or | |||
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of | |||
* text in the original document. However, multiple edits can have | |||
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first. | |||
* To apply edits to an input, you can use `applyEdits` | |||
*/ | |||
export function format(documentText: string, range: Range, options: FormattingOptions): Edit[]; | |||
/** | |||
* Computes the edits needed to modify a value in the JSON document. | |||
* | |||
* @param documentText The input text | |||
* @param path The path of the value to change. The path represents either to the document root, a property or an array item. | |||
* If the path points to an non-existing property or item, it will be created. | |||
* @param value The new value for the specified property or item. If the value is undefined, | |||
* the property or item will be removed. | |||
* @param options Options | |||
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or | |||
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of | |||
* text in the original document. However, multiple edits can have | |||
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first. | |||
* To apply edits to an input, you can use `applyEdits` | |||
*/ | |||
export function modify(text: string, path: JSONPath, value: any, options: ModificationOptions): Edit[]; | |||
/** | |||
* Applies edits to a input string. | |||
*/ | |||
export function applyEdits(text: string, edits: Edit[]): string; | |||
/** | |||
* Represents a text modification | |||
*/ | |||
export interface Edit { | |||
/** | |||
* The start offset of the modification. | |||
*/ | |||
offset: number; | |||
/** | |||
* The length of the modification. Must not be negative. Empty length represents an *insert*. | |||
*/ | |||
length: number; | |||
/** | |||
* The new content. Empty content represents a *remove*. | |||
*/ | |||
content: string; | |||
} | |||
/** | |||
* A text range in the document | |||
*/ | |||
export interface Range { | |||
/** | |||
* The start offset of the range. | |||
*/ | |||
offset: number; | |||
/** | |||
* The length of the range. Must not be negative. | |||
*/ | |||
length: number; | |||
} | |||
export interface FormattingOptions { | |||
/** | |||
* If indentation is based on spaces (`insertSpaces` = true), then what is the number of spaces that make an indent? | |||
*/ | |||
tabSize: number; | |||
/** | |||
* Is indentation based on spaces? | |||
*/ | |||
insertSpaces: boolean; | |||
/** | |||
* The default 'end of line' character | |||
*/ | |||
eol: string; | |||
} | |||
``` | |||
License | |||
------- | |||
(MIT License) | |||
Copyright 2018, Microsoft |
@@ -1,5 +0,0 @@ | |||
import { Edit, FormattingOptions, JSONPath } from './main'; | |||
export declare function removeProperty(text: string, path: JSONPath, formattingOptions: FormattingOptions): Edit[]; | |||
export declare function setProperty(text: string, path: JSONPath, value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number): Edit[]; | |||
export declare function applyEdit(text: string, edit: Edit): string; | |||
export declare function isWS(text: string, offset: number): boolean; |
@@ -1,168 +0,0 @@ | |||
/*--------------------------------------------------------------------------------------------- | |||
* Copyright (c) Microsoft Corporation. All rights reserved. | |||
* Licensed under the MIT License. See License.txt in the project root for license information. | |||
*--------------------------------------------------------------------------------------------*/ | |||
'use strict'; | |||
import { parseTree, findNodeAtLocation } from './main'; | |||
import { format, isEOL } from './format'; | |||
export function removeProperty(text, path, formattingOptions) { | |||
return setProperty(text, path, void 0, formattingOptions); | |||
} | |||
export function setProperty(text, path, value, formattingOptions, getInsertionIndex) { | |||
var errors = []; | |||
var root = parseTree(text, errors); | |||
var parent = void 0; | |||
var lastSegment = void 0; | |||
while (path.length > 0) { | |||
lastSegment = path.pop(); | |||
parent = findNodeAtLocation(root, path); | |||
if (parent === void 0 && value !== void 0) { | |||
if (typeof lastSegment === 'string') { | |||
value = (_a = {}, _a[lastSegment] = value, _a); | |||
} | |||
else { | |||
value = [value]; | |||
} | |||
} | |||
else { | |||
break; | |||
} | |||
} | |||
if (!parent) { | |||
// empty document | |||
if (value === void 0) { | |||
throw new Error('Can not delete in empty document'); | |||
} | |||
return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, formattingOptions); | |||
} | |||
else if (parent.type === 'object' && typeof lastSegment === 'string' && Array.isArray(parent.children)) { | |||
var existing = findNodeAtLocation(parent, [lastSegment]); | |||
if (existing !== void 0) { | |||
if (value === void 0) { | |||
if (!existing.parent) { | |||
throw new Error('Malformed AST'); | |||
} | |||
var propertyIndex = parent.children.indexOf(existing.parent); | |||
var removeBegin = void 0; | |||
var removeEnd = existing.parent.offset + existing.parent.length; | |||
if (propertyIndex > 0) { | |||
// remove the comma of the previous node | |||
var previous = parent.children[propertyIndex - 1]; | |||
removeBegin = previous.offset + previous.length; | |||
} | |||
else { | |||
removeBegin = parent.offset + 1; | |||
if (parent.children.length > 1) { | |||
// remove the comma of the next node | |||
var next = parent.children[1]; | |||
removeEnd = next.offset; | |||
} | |||
} | |||
return withFormatting(text, { offset: removeBegin, length: removeEnd - removeBegin, content: '' }, formattingOptions); | |||
} | |||
else { | |||
// set value of existing property | |||
return withFormatting(text, { offset: existing.offset, length: existing.length, content: JSON.stringify(value) }, formattingOptions); | |||
} | |||
} | |||
else { | |||
if (value === void 0) { | |||
return []; // property does not exist, nothing to do | |||
} | |||
var newProperty = JSON.stringify(lastSegment) + ": " + JSON.stringify(value); | |||
var index = getInsertionIndex ? getInsertionIndex(parent.children.map(function (p) { return p.children[0].value; })) : parent.children.length; | |||
var edit = void 0; | |||
if (index > 0) { | |||
var previous = parent.children[index - 1]; | |||
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty }; | |||
} | |||
else if (parent.children.length === 0) { | |||
edit = { offset: parent.offset + 1, length: 0, content: newProperty }; | |||
} | |||
else { | |||
edit = { offset: parent.offset + 1, length: 0, content: newProperty + ',' }; | |||
} | |||
return withFormatting(text, edit, formattingOptions); | |||
} | |||
} | |||
else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) { | |||
var insertIndex = lastSegment; | |||
if (insertIndex === -1) { | |||
// Insert | |||
var newProperty = "" + JSON.stringify(value); | |||
var edit = void 0; | |||
if (parent.children.length === 0) { | |||
edit = { offset: parent.offset + 1, length: 0, content: newProperty }; | |||
} | |||
else { | |||
var previous = parent.children[parent.children.length - 1]; | |||
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty }; | |||
} | |||
return withFormatting(text, edit, formattingOptions); | |||
} | |||
else { | |||
if (value === void 0 && parent.children.length >= 0) { | |||
//Removal | |||
var removalIndex = lastSegment; | |||
var toRemove = parent.children[removalIndex]; | |||
var edit = void 0; | |||
if (parent.children.length === 1) { | |||
// only item | |||
edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' }; | |||
} | |||
else if (parent.children.length - 1 === removalIndex) { | |||
// last item | |||
var previous = parent.children[removalIndex - 1]; | |||
var offset = previous.offset + previous.length; | |||
var parentEndOffset = parent.offset + parent.length; | |||
edit = { offset: offset, length: parentEndOffset - 2 - offset, content: '' }; | |||
} | |||
else { | |||
edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' }; | |||
} | |||
return withFormatting(text, edit, formattingOptions); | |||
} | |||
else { | |||
throw new Error('Array modification not supported yet'); | |||
} | |||
} | |||
} | |||
else { | |||
throw new Error("Can not add " + (typeof lastSegment !== 'number' ? 'index' : 'property') + " to parent of type " + parent.type); | |||
} | |||
var _a; | |||
} | |||
function withFormatting(text, edit, formattingOptions) { | |||
// apply the edit | |||
var newText = applyEdit(text, edit); | |||
// format the new text | |||
var begin = edit.offset; | |||
var end = edit.offset + edit.content.length; | |||
if (edit.length === 0 || edit.content.length === 0) { | |||
while (begin > 0 && !isEOL(newText, begin - 1)) { | |||
begin--; | |||
} | |||
while (end < newText.length && !isEOL(newText, end)) { | |||
end++; | |||
} | |||
} | |||
var edits = format(newText, { offset: begin, length: end - begin }, formattingOptions); | |||
// apply the formatting edits and track the begin and end offsets of the changes | |||
for (var i = edits.length - 1; i >= 0; i--) { | |||
var edit_1 = edits[i]; | |||
newText = applyEdit(newText, edit_1); | |||
begin = Math.min(begin, edit_1.offset); | |||
end = Math.max(end, edit_1.offset + edit_1.length); | |||
end += edit_1.content.length - edit_1.length; | |||
} | |||
// create a single edit with all changes | |||
var editLength = text.length - (newText.length - end) - begin; | |||
return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }]; | |||
} | |||
export function applyEdit(text, edit) { | |||
return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length); | |||
} | |||
export function isWS(text, offset) { | |||
return '\r\n \t'.indexOf(text.charAt(offset)) !== -1; | |||
} | |||
//# sourceMappingURL=edit.js.map |
@@ -1,3 +0,0 @@ | |||
import * as Json from './main'; | |||
export declare function format(documentText: string, range: Json.Range | undefined, options: Json.FormattingOptions): Json.Edit[]; | |||
export declare function isEOL(text: string, offset: number): boolean; |
@@ -1,195 +0,0 @@ | |||
/*--------------------------------------------------------------------------------------------- | |||
* Copyright (c) Microsoft Corporation. All rights reserved. | |||
* Licensed under the MIT License. See License.txt in the project root for license information. | |||
*--------------------------------------------------------------------------------------------*/ | |||
'use strict'; | |||
import * as Json from './main'; | |||
export function format(documentText, range, options) { | |||
var initialIndentLevel; | |||
var formatText; | |||
var formatTextStart; | |||
var rangeStart; | |||
var rangeEnd; | |||
if (range) { | |||
rangeStart = range.offset; | |||
rangeEnd = rangeStart + range.length; | |||
formatTextStart = rangeStart; | |||
while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) { | |||
formatTextStart--; | |||
} | |||
var endOffset = rangeEnd; | |||
while (endOffset < documentText.length && !isEOL(documentText, endOffset)) { | |||
endOffset++; | |||
} | |||
formatText = documentText.substring(formatTextStart, endOffset); | |||
initialIndentLevel = computeIndentLevel(formatText, 0, options); | |||
} | |||
else { | |||
formatText = documentText; | |||
initialIndentLevel = 0; | |||
formatTextStart = 0; | |||
rangeStart = 0; | |||
rangeEnd = documentText.length; | |||
} | |||
var eol = getEOL(options, documentText); | |||
var lineBreak = false; | |||
var indentLevel = 0; | |||
var indentValue; | |||
if (options.insertSpaces) { | |||
indentValue = repeat(' ', options.tabSize || 4); | |||
} | |||
else { | |||
indentValue = '\t'; | |||
} | |||
var scanner = Json.createScanner(formatText, false); | |||
var hasError = false; | |||
function newLineAndIndent() { | |||
return eol + repeat(indentValue, initialIndentLevel + indentLevel); | |||
} | |||
function scanNext() { | |||
var token = scanner.scan(); | |||
lineBreak = false; | |||
while (token === Json.SyntaxKind.Trivia || token === Json.SyntaxKind.LineBreakTrivia) { | |||
lineBreak = lineBreak || (token === Json.SyntaxKind.LineBreakTrivia); | |||
token = scanner.scan(); | |||
} | |||
hasError = token === Json.SyntaxKind.Unknown || scanner.getTokenError() !== Json.ScanError.None; | |||
return token; | |||
} | |||
var editOperations = []; | |||
function addEdit(text, startOffset, endOffset) { | |||
if (!hasError && startOffset < rangeEnd && endOffset > rangeStart && documentText.substring(startOffset, endOffset) !== text) { | |||
editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text }); | |||
} | |||
} | |||
var firstToken = scanNext(); | |||
if (firstToken !== Json.SyntaxKind.EOF) { | |||
var firstTokenStart = scanner.getTokenOffset() + formatTextStart; | |||
var initialIndent = repeat(indentValue, initialIndentLevel); | |||
addEdit(initialIndent, formatTextStart, firstTokenStart); | |||
} | |||
while (firstToken !== Json.SyntaxKind.EOF) { | |||
var firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart; | |||
var secondToken = scanNext(); | |||
var replaceContent = ''; | |||
while (!lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) { | |||
// comments on the same line: keep them on the same line, but ignore them otherwise | |||
var commentTokenStart = scanner.getTokenOffset() + formatTextStart; | |||
addEdit(' ', firstTokenEnd, commentTokenStart); | |||
firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart; | |||
replaceContent = secondToken === Json.SyntaxKind.LineCommentTrivia ? newLineAndIndent() : ''; | |||
secondToken = scanNext(); | |||
} | |||
if (secondToken === Json.SyntaxKind.CloseBraceToken) { | |||
if (firstToken !== Json.SyntaxKind.OpenBraceToken) { | |||
indentLevel--; | |||
replaceContent = newLineAndIndent(); | |||
} | |||
} | |||
else if (secondToken === Json.SyntaxKind.CloseBracketToken) { | |||
if (firstToken !== Json.SyntaxKind.OpenBracketToken) { | |||
indentLevel--; | |||
replaceContent = newLineAndIndent(); | |||
} | |||
} | |||
else { | |||
switch (firstToken) { | |||
case Json.SyntaxKind.OpenBracketToken: | |||
case Json.SyntaxKind.OpenBraceToken: | |||
indentLevel++; | |||
replaceContent = newLineAndIndent(); | |||
break; | |||
case Json.SyntaxKind.CommaToken: | |||
case Json.SyntaxKind.LineCommentTrivia: | |||
replaceContent = newLineAndIndent(); | |||
break; | |||
case Json.SyntaxKind.BlockCommentTrivia: | |||
if (lineBreak) { | |||
replaceContent = newLineAndIndent(); | |||
} | |||
else { | |||
// symbol following comment on the same line: keep on same line, separate with ' ' | |||
replaceContent = ' '; | |||
} | |||
break; | |||
case Json.SyntaxKind.ColonToken: | |||
replaceContent = ' '; | |||
break; | |||
case Json.SyntaxKind.StringLiteral: | |||
if (secondToken === Json.SyntaxKind.ColonToken) { | |||
replaceContent = ''; | |||
break; | |||
} | |||
// fall through | |||
case Json.SyntaxKind.NullKeyword: | |||
case Json.SyntaxKind.TrueKeyword: | |||
case Json.SyntaxKind.FalseKeyword: | |||
case Json.SyntaxKind.NumericLiteral: | |||
case Json.SyntaxKind.CloseBraceToken: | |||
case Json.SyntaxKind.CloseBracketToken: | |||
if (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia) { | |||
replaceContent = ' '; | |||
} | |||
else if (secondToken !== Json.SyntaxKind.CommaToken && secondToken !== Json.SyntaxKind.EOF) { | |||
hasError = true; | |||
} | |||
break; | |||
case Json.SyntaxKind.Unknown: | |||
hasError = true; | |||
break; | |||
} | |||
if (lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) { | |||
replaceContent = newLineAndIndent(); | |||
} | |||
} | |||
var secondTokenStart = scanner.getTokenOffset() + formatTextStart; | |||
addEdit(replaceContent, firstTokenEnd, secondTokenStart); | |||
firstToken = secondToken; | |||
} | |||
return editOperations; | |||
} | |||
function repeat(s, count) { | |||
var result = ''; | |||
for (var i = 0; i < count; i++) { | |||
result += s; | |||
} | |||
return result; | |||
} | |||
function computeIndentLevel(content, offset, options) { | |||
var i = 0; | |||
var nChars = 0; | |||
var tabSize = options.tabSize || 4; | |||
while (i < content.length) { | |||
var ch = content.charAt(i); | |||
if (ch === ' ') { | |||
nChars++; | |||
} | |||
else if (ch === '\t') { | |||
nChars += tabSize; | |||
} | |||
else { | |||
break; | |||
} | |||
i++; | |||
} | |||
return Math.floor(nChars / tabSize); | |||
} | |||
function getEOL(options, text) { | |||
for (var i = 0; i < text.length; i++) { | |||
var ch = text.charAt(i); | |||
if (ch === '\r') { | |||
if (i + 1 < text.length && text.charAt(i + 1) === '\n') { | |||
return '\r\n'; | |||
} | |||
return '\r'; | |||
} | |||
else if (ch === '\n') { | |||
return '\n'; | |||
} | |||
} | |||
return (options && options.eol) || '\n'; | |||
} | |||
export function isEOL(text, offset) { | |||
return '\r\n'.indexOf(text.charAt(offset)) !== -1; | |||
} | |||
//# sourceMappingURL=format.js.map |
@@ -1,289 +0,0 @@ | |||
export declare enum ScanError { | |||
None = 0, | |||
UnexpectedEndOfComment = 1, | |||
UnexpectedEndOfString = 2, | |||
UnexpectedEndOfNumber = 3, | |||
InvalidUnicode = 4, | |||
InvalidEscapeCharacter = 5, | |||
InvalidCharacter = 6, | |||
} | |||
export declare enum SyntaxKind { | |||
Unknown = 0, | |||
OpenBraceToken = 1, | |||
CloseBraceToken = 2, | |||
OpenBracketToken = 3, | |||
CloseBracketToken = 4, | |||
CommaToken = 5, | |||
ColonToken = 6, | |||
NullKeyword = 7, | |||
TrueKeyword = 8, | |||
FalseKeyword = 9, | |||
StringLiteral = 10, | |||
NumericLiteral = 11, | |||
LineCommentTrivia = 12, | |||
BlockCommentTrivia = 13, | |||
LineBreakTrivia = 14, | |||
Trivia = 15, | |||
EOF = 16, | |||
} | |||
/** | |||
* The scanner object, representing a JSON scanner at a position in the input string. | |||
*/ | |||
export interface JSONScanner { | |||
/** | |||
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token. | |||
*/ | |||
setPosition(pos: number): void; | |||
/** | |||
* Read the next token. Returns the tolen code. | |||
*/ | |||
scan(): SyntaxKind; | |||
/** | |||
* Returns the current scan position, which is after the last read token. | |||
*/ | |||
getPosition(): number; | |||
/** | |||
* Returns the last read token. | |||
*/ | |||
getToken(): SyntaxKind; | |||
/** | |||
* Returns the last read token value. The value for strings is the decoded string content. For numbers its of type number, for boolean it's true or false. | |||
*/ | |||
getTokenValue(): string; | |||
/** | |||
* The start offset of the last read token. | |||
*/ | |||
getTokenOffset(): number; | |||
/** | |||
* The length of the last read token. | |||
*/ | |||
getTokenLength(): number; | |||
/** | |||
* An error code of the last scan. | |||
*/ | |||
getTokenError(): ScanError; | |||
} | |||
/** | |||
* Creates a JSON scanner on the given text. | |||
* If ignoreTrivia is set, whitespaces or comments are ignored. | |||
*/ | |||
export declare function createScanner(text: string, ignoreTrivia?: boolean): JSONScanner; | |||
/** | |||
* Takes JSON with JavaScript-style comments and remove | |||
* them. Optionally replaces every none-newline character | |||
* of comments with a replaceCharacter | |||
*/ | |||
export declare function stripComments(text: string, replaceCh?: string): string; | |||
export interface ParseError { | |||
error: ParseErrorCode; | |||
offset: number; | |||
length: number; | |||
} | |||
export declare enum ParseErrorCode { | |||
InvalidSymbol = 0, | |||
InvalidNumberFormat = 1, | |||
PropertyNameExpected = 2, | |||
ValueExpected = 3, | |||
ColonExpected = 4, | |||
CommaExpected = 5, | |||
CloseBraceExpected = 6, | |||
CloseBracketExpected = 7, | |||
EndOfFileExpected = 8, | |||
InvalidCommentToken = 9, | |||
UnexpectedEndOfComment = 10, | |||
UnexpectedEndOfString = 11, | |||
UnexpectedEndOfNumber = 12, | |||
InvalidUnicode = 13, | |||
InvalidEscapeCharacter = 14, | |||
InvalidCharacter = 15, | |||
} | |||
export declare type NodeType = 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null'; | |||
export interface Node { | |||
type: NodeType; | |||
value?: any; | |||
offset: number; | |||
length: number; | |||
columnOffset?: number; | |||
parent?: Node; | |||
children?: Node[]; | |||
} | |||
export declare type Segment = string | number; | |||
export declare type JSONPath = Segment[]; | |||
export interface Location { | |||
/** | |||
* The previous property key or literal value (string, number, boolean or null) or undefined. | |||
*/ | |||
previousNode?: Node; | |||
/** | |||
* The path describing the location in the JSON document. The path consists of a sequence strings | |||
* representing an object property or numbers for array indices. | |||
*/ | |||
path: JSONPath; | |||
/** | |||
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices). | |||
* '*' will match a single segment, of any property name or index. | |||
* '**' will match a sequece of segments or no segment, of any property name or index. | |||
*/ | |||
matches: (patterns: JSONPath) => boolean; | |||
/** | |||
* If set, the location's offset is at a property key. | |||
*/ | |||
isAtPropertyKey: boolean; | |||
} | |||
/** | |||
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index. | |||
*/ | |||
export declare function getLocation(text: string, position: number): Location; | |||
export interface ParseOptions { | |||
disallowComments?: boolean; | |||
allowTrailingComma?: boolean; | |||
} | |||
/** | |||
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. | |||
* Therefore always check the errors list to find out if the input was valid. | |||
*/ | |||
export declare function parse(text: string, errors?: ParseError[], options?: ParseOptions): any; | |||
/** | |||
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. | |||
*/ | |||
export declare function parseTree(text: string, errors?: ParseError[], options?: ParseOptions): Node; | |||
/** | |||
* Finds the node at the given path in a JSON DOM. | |||
*/ | |||
export declare function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined; | |||
/** | |||
* Evaluates the JavaScript object of the given JSON DOM node | |||
*/ | |||
export declare function getNodeValue(node: Node): any; | |||
/** | |||
* Parses the given text and invokes the visitor functions for each object, array and literal reached. | |||
*/ | |||
export declare function visit(text: string, visitor: JSONVisitor, options?: ParseOptions): any; | |||
export interface JSONVisitor { | |||
/** | |||
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace. | |||
*/ | |||
onObjectBegin?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked when a property is encountered. The offset and length represent the location of the property name. | |||
*/ | |||
onObjectProperty?: (property: string, offset: number, length: number) => void; | |||
/** | |||
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace. | |||
*/ | |||
onObjectEnd?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket. | |||
*/ | |||
onArrayBegin?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket. | |||
*/ | |||
onArrayEnd?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value. | |||
*/ | |||
onLiteralValue?: (value: any, offset: number, length: number) => void; | |||
/** | |||
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator. | |||
*/ | |||
onSeparator?: (character: string, offset: number, length: number) => void; | |||
/** | |||
* When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment. | |||
*/ | |||
onComment?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked on an error. | |||
*/ | |||
onError?: (error: ParseErrorCode, offset: number, length: number) => void; | |||
} | |||
/** | |||
* Represents a text modification | |||
*/ | |||
export interface Edit { | |||
/** | |||
* The start offset of the modification. | |||
*/ | |||
offset: number; | |||
/** | |||
* The length of the modification. Must not be negative. Empty length represents an *insert*. | |||
*/ | |||
length: number; | |||
/** | |||
* The new content. Empty content represents a *remove*. | |||
*/ | |||
content: string; | |||
} | |||
/** | |||
* A text range in the document | |||
*/ | |||
export interface Range { | |||
/** | |||
* The start offset of the range. | |||
*/ | |||
offset: number; | |||
/** | |||
* The length of the range. Must not be negative. | |||
*/ | |||
length: number; | |||
} | |||
export interface FormattingOptions { | |||
/** | |||
* If indentation is based on spaces (`insertSpaces` = true), then what is the number of spaces that make an indent? | |||
*/ | |||
tabSize?: number; | |||
/** | |||
* Is indentation based on spaces? | |||
*/ | |||
insertSpaces?: boolean; | |||
/** | |||
* The default 'end of line' character. If not set, '\n' is used as default. | |||
*/ | |||
eol?: string; | |||
} | |||
/** | |||
* Computes the edits needed to format a JSON document. | |||
* | |||
* @param documentText The input text | |||
* @param range The range to format or `undefined` to format the full content | |||
* @param options The formatting options | |||
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or | |||
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of | |||
* text in the original document. However, multiple edits can have | |||
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first. | |||
* To apply edits to an input, you can use `applyEdits` | |||
*/ | |||
export declare function format(documentText: string, range: Range | undefined, options: FormattingOptions): Edit[]; | |||
/** | |||
* Options used when computing the modification edits | |||
*/ | |||
export interface ModificationOptions { | |||
/** | |||
* Formatting options | |||
*/ | |||
formattingOptions: FormattingOptions; | |||
/** | |||
* Optional fucntion to define the insertion index given an existing list of properties. | |||
*/ | |||
getInsertionIndex?: (properties: string[]) => number; | |||
} | |||
/** | |||
* Computes the edits needed to modify a value in the JSON document. | |||
* | |||
* @param documentText The input text | |||
* @param path The path of the value to change. The path represents either to the document root, a property or an array item. | |||
* If the path points to an non-existing property or item, it will be created. | |||
* @param value The new value for the specified property or item. If the value is undefined, | |||
* the property or item will be removed. | |||
* @param options Options | |||
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or | |||
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of | |||
* text in the original document. However, multiple edits can have | |||
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first. | |||
* To apply edits to an input, you can use `applyEdits` | |||
*/ | |||
export declare function modify(text: string, path: JSONPath, value: any, options: ModificationOptions): Edit[]; | |||
/** | |||
* Applies edits to a input string. | |||
*/ | |||
export declare function applyEdits(text: string, edits: Edit[]): string; |
@@ -1,985 +0,0 @@ | |||
/*--------------------------------------------------------------------------------------------- | |||
* Copyright (c) Microsoft Corporation. All rights reserved. | |||
* Licensed under the MIT License. See License.txt in the project root for license information. | |||
*--------------------------------------------------------------------------------------------*/ | |||
'use strict'; | |||
import { format as _format } from './format'; | |||
import { setProperty, applyEdit } from './edit'; | |||
export var ScanError; | |||
(function (ScanError) { | |||
ScanError[ScanError["None"] = 0] = "None"; | |||
ScanError[ScanError["UnexpectedEndOfComment"] = 1] = "UnexpectedEndOfComment"; | |||
ScanError[ScanError["UnexpectedEndOfString"] = 2] = "UnexpectedEndOfString"; | |||
ScanError[ScanError["UnexpectedEndOfNumber"] = 3] = "UnexpectedEndOfNumber"; | |||
ScanError[ScanError["InvalidUnicode"] = 4] = "InvalidUnicode"; | |||
ScanError[ScanError["InvalidEscapeCharacter"] = 5] = "InvalidEscapeCharacter"; | |||
ScanError[ScanError["InvalidCharacter"] = 6] = "InvalidCharacter"; | |||
})(ScanError || (ScanError = {})); | |||
export var SyntaxKind; | |||
(function (SyntaxKind) { | |||
SyntaxKind[SyntaxKind["Unknown"] = 0] = "Unknown"; | |||
SyntaxKind[SyntaxKind["OpenBraceToken"] = 1] = "OpenBraceToken"; | |||
SyntaxKind[SyntaxKind["CloseBraceToken"] = 2] = "CloseBraceToken"; | |||
SyntaxKind[SyntaxKind["OpenBracketToken"] = 3] = "OpenBracketToken"; | |||
SyntaxKind[SyntaxKind["CloseBracketToken"] = 4] = "CloseBracketToken"; | |||
SyntaxKind[SyntaxKind["CommaToken"] = 5] = "CommaToken"; | |||
SyntaxKind[SyntaxKind["ColonToken"] = 6] = "ColonToken"; | |||
SyntaxKind[SyntaxKind["NullKeyword"] = 7] = "NullKeyword"; | |||
SyntaxKind[SyntaxKind["TrueKeyword"] = 8] = "TrueKeyword"; | |||
SyntaxKind[SyntaxKind["FalseKeyword"] = 9] = "FalseKeyword"; | |||
SyntaxKind[SyntaxKind["StringLiteral"] = 10] = "StringLiteral"; | |||
SyntaxKind[SyntaxKind["NumericLiteral"] = 11] = "NumericLiteral"; | |||
SyntaxKind[SyntaxKind["LineCommentTrivia"] = 12] = "LineCommentTrivia"; | |||
SyntaxKind[SyntaxKind["BlockCommentTrivia"] = 13] = "BlockCommentTrivia"; | |||
SyntaxKind[SyntaxKind["LineBreakTrivia"] = 14] = "LineBreakTrivia"; | |||
SyntaxKind[SyntaxKind["Trivia"] = 15] = "Trivia"; | |||
SyntaxKind[SyntaxKind["EOF"] = 16] = "EOF"; | |||
})(SyntaxKind || (SyntaxKind = {})); | |||
/** | |||
* Creates a JSON scanner on the given text. | |||
* If ignoreTrivia is set, whitespaces or comments are ignored. | |||
*/ | |||
export function createScanner(text, ignoreTrivia) { | |||
if (ignoreTrivia === void 0) { ignoreTrivia = false; } | |||
var pos = 0, len = text.length, value = '', tokenOffset = 0, token = SyntaxKind.Unknown, scanError = ScanError.None; | |||
function scanHexDigits(count, exact) { | |||
var digits = 0; | |||
var value = 0; | |||
while (digits < count || !exact) { | |||
var ch = text.charCodeAt(pos); | |||
if (ch >= 48 /* _0 */ && ch <= 57 /* _9 */) { | |||
value = value * 16 + ch - 48 /* _0 */; | |||
} | |||
else if (ch >= 65 /* A */ && ch <= 70 /* F */) { | |||
value = value * 16 + ch - 65 /* A */ + 10; | |||
} | |||
else if (ch >= 97 /* a */ && ch <= 102 /* f */) { | |||
value = value * 16 + ch - 97 /* a */ + 10; | |||
} | |||
else { | |||
break; | |||
} | |||
pos++; | |||
digits++; | |||
} | |||
if (digits < count) { | |||
value = -1; | |||
} | |||
return value; | |||
} | |||
function setPosition(newPosition) { | |||
pos = newPosition; | |||
value = ''; | |||
tokenOffset = 0; | |||
token = SyntaxKind.Unknown; | |||
scanError = ScanError.None; | |||
} | |||
function scanNumber() { | |||
var start = pos; | |||
if (text.charCodeAt(pos) === 48 /* _0 */) { | |||
pos++; | |||
} | |||
else { | |||
pos++; | |||
while (pos < text.length && isDigit(text.charCodeAt(pos))) { | |||
pos++; | |||
} | |||
} | |||
if (pos < text.length && text.charCodeAt(pos) === 46 /* dot */) { | |||
pos++; | |||
if (pos < text.length && isDigit(text.charCodeAt(pos))) { | |||
pos++; | |||
while (pos < text.length && isDigit(text.charCodeAt(pos))) { | |||
pos++; | |||
} | |||
} | |||
else { | |||
scanError = ScanError.UnexpectedEndOfNumber; | |||
return text.substring(start, pos); | |||
} | |||
} | |||
var end = pos; | |||
if (pos < text.length && (text.charCodeAt(pos) === 69 /* E */ || text.charCodeAt(pos) === 101 /* e */)) { | |||
pos++; | |||
if (pos < text.length && text.charCodeAt(pos) === 43 /* plus */ || text.charCodeAt(pos) === 45 /* minus */) { | |||
pos++; | |||
} | |||
if (pos < text.length && isDigit(text.charCodeAt(pos))) { | |||
pos++; | |||
while (pos < text.length && isDigit(text.charCodeAt(pos))) { | |||
pos++; | |||
} | |||
end = pos; | |||
} | |||
else { | |||
scanError = ScanError.UnexpectedEndOfNumber; | |||
} | |||
} | |||
return text.substring(start, end); | |||
} | |||
function scanString() { | |||
var result = '', start = pos; | |||
while (true) { | |||
if (pos >= len) { | |||
result += text.substring(start, pos); | |||
scanError = ScanError.UnexpectedEndOfString; | |||
break; | |||
} | |||
var ch = text.charCodeAt(pos); | |||
if (ch === 34 /* doubleQuote */) { | |||
result += text.substring(start, pos); | |||
pos++; | |||
break; | |||
} | |||
if (ch === 92 /* backslash */) { | |||
result += text.substring(start, pos); | |||
pos++; | |||
if (pos >= len) { | |||
scanError = ScanError.UnexpectedEndOfString; | |||
break; | |||
} | |||
ch = text.charCodeAt(pos++); | |||
switch (ch) { | |||
case 34 /* doubleQuote */: | |||
result += '\"'; | |||
break; | |||
case 92 /* backslash */: | |||
result += '\\'; | |||
break; | |||
case 47 /* slash */: | |||
result += '/'; | |||
break; | |||
case 98 /* b */: | |||
result += '\b'; | |||
break; | |||
case 102 /* f */: | |||
result += '\f'; | |||
break; | |||
case 110 /* n */: | |||
result += '\n'; | |||
break; | |||
case 114 /* r */: | |||
result += '\r'; | |||
break; | |||
case 116 /* t */: | |||
result += '\t'; | |||
break; | |||
case 117 /* u */: | |||
var ch_1 = scanHexDigits(4, true); | |||
if (ch_1 >= 0) { | |||
result += String.fromCharCode(ch_1); | |||
} | |||
else { | |||
scanError = ScanError.InvalidUnicode; | |||
} | |||
break; | |||
default: | |||
scanError = ScanError.InvalidEscapeCharacter; | |||
} | |||
start = pos; | |||
continue; | |||
} | |||
if (ch >= 0 && ch <= 0x1f) { | |||
if (isLineBreak(ch)) { | |||
result += text.substring(start, pos); | |||
scanError = ScanError.UnexpectedEndOfString; | |||
break; | |||
} | |||
else { | |||
scanError = ScanError.InvalidCharacter; | |||
// mark as error but continue with string | |||
} | |||
} | |||
pos++; | |||
} | |||
return result; | |||
} | |||
function scanNext() { | |||
value = ''; | |||
scanError = ScanError.None; | |||
tokenOffset = pos; | |||
if (pos >= len) { | |||
// at the end | |||
tokenOffset = len; | |||
return token = SyntaxKind.EOF; | |||
} | |||
var code = text.charCodeAt(pos); | |||
// trivia: whitespace | |||
if (isWhiteSpace(code)) { | |||
do { | |||
pos++; | |||
value += String.fromCharCode(code); | |||
code = text.charCodeAt(pos); | |||
} while (isWhiteSpace(code)); | |||
return token = SyntaxKind.Trivia; | |||
} | |||
// trivia: newlines | |||
if (isLineBreak(code)) { | |||
pos++; | |||
value += String.fromCharCode(code); | |||
if (code === 13 /* carriageReturn */ && text.charCodeAt(pos) === 10 /* lineFeed */) { | |||
pos++; | |||
value += '\n'; | |||
} | |||
return token = SyntaxKind.LineBreakTrivia; | |||
} | |||
switch (code) { | |||
// tokens: []{}:, | |||
case 123 /* openBrace */: | |||
pos++; | |||
return token = SyntaxKind.OpenBraceToken; | |||
case 125 /* closeBrace */: | |||
pos++; | |||
return token = SyntaxKind.CloseBraceToken; | |||
case 91 /* openBracket */: | |||
pos++; | |||
return token = SyntaxKind.OpenBracketToken; | |||
case 93 /* closeBracket */: | |||
pos++; | |||
return token = SyntaxKind.CloseBracketToken; | |||
case 58 /* colon */: | |||
pos++; | |||
return token = SyntaxKind.ColonToken; | |||
case 44 /* comma */: | |||
pos++; | |||
return token = SyntaxKind.CommaToken; | |||
// strings | |||
case 34 /* doubleQuote */: | |||
pos++; | |||
value = scanString(); | |||
return token = SyntaxKind.StringLiteral; | |||
// comments | |||
case 47 /* slash */: | |||
var start = pos - 1; | |||
// Single-line comment | |||
if (text.charCodeAt(pos + 1) === 47 /* slash */) { | |||
pos += 2; | |||
while (pos < len) { | |||
if (isLineBreak(text.charCodeAt(pos))) { | |||
break; | |||
} | |||
pos++; | |||
} | |||
value = text.substring(start, pos); | |||
return token = SyntaxKind.LineCommentTrivia; | |||
} | |||
// Multi-line comment | |||
if (text.charCodeAt(pos + 1) === 42 /* asterisk */) { | |||
pos += 2; | |||
var commentClosed = false; | |||
while (pos < len) { | |||
var ch = text.charCodeAt(pos); | |||
if (ch === 42 /* asterisk */ && (pos + 1 < len) && text.charCodeAt(pos + 1) === 47 /* slash */) { | |||
pos += 2; | |||
commentClosed = true; | |||
break; | |||
} | |||
pos++; | |||
} | |||
if (!commentClosed) { | |||
pos++; | |||
scanError = ScanError.UnexpectedEndOfComment; | |||
} | |||
value = text.substring(start, pos); | |||
return token = SyntaxKind.BlockCommentTrivia; | |||
} | |||
// just a single slash | |||
value += String.fromCharCode(code); | |||
pos++; | |||
return token = SyntaxKind.Unknown; | |||
// numbers | |||
case 45 /* minus */: | |||
value += String.fromCharCode(code); | |||
pos++; | |||
if (pos === len || !isDigit(text.charCodeAt(pos))) { | |||
return token = SyntaxKind.Unknown; | |||
} | |||
// found a minus, followed by a number so | |||
// we fall through to proceed with scanning | |||
// numbers | |||
case 48 /* _0 */: | |||
case 49 /* _1 */: | |||
case 50 /* _2 */: | |||
case 51 /* _3 */: | |||
case 52 /* _4 */: | |||
case 53 /* _5 */: | |||
case 54 /* _6 */: | |||
case 55 /* _7 */: | |||
case 56 /* _8 */: | |||
case 57 /* _9 */: | |||
value += scanNumber(); | |||
return token = SyntaxKind.NumericLiteral; | |||
// literals and unknown symbols | |||
default: | |||
// is a literal? Read the full word. | |||
while (pos < len && isUnknownContentCharacter(code)) { | |||
pos++; | |||
code = text.charCodeAt(pos); | |||
} | |||
if (tokenOffset !== pos) { | |||
value = text.substring(tokenOffset, pos); | |||
// keywords: true, false, null | |||
switch (value) { | |||
case 'true': return token = SyntaxKind.TrueKeyword; | |||
case 'false': return token = SyntaxKind.FalseKeyword; | |||
case 'null': return token = SyntaxKind.NullKeyword; | |||
} | |||
return token = SyntaxKind.Unknown; | |||
} | |||
// some | |||
value += String.fromCharCode(code); | |||
pos++; | |||
return token = SyntaxKind.Unknown; | |||
} | |||
} | |||
function isUnknownContentCharacter(code) { | |||
if (isWhiteSpace(code) || isLineBreak(code)) { | |||
return false; | |||
} | |||
switch (code) { | |||
case 125 /* closeBrace */: | |||
case 93 /* closeBracket */: | |||
case 123 /* openBrace */: | |||
case 91 /* openBracket */: | |||
case 34 /* doubleQuote */: | |||
case 58 /* colon */: | |||
case 44 /* comma */: | |||
return false; | |||
} | |||
return true; | |||
} | |||
function scanNextNonTrivia() { | |||
var result; | |||
do { | |||
result = scanNext(); | |||
} while (result >= SyntaxKind.LineCommentTrivia && result <= SyntaxKind.Trivia); | |||
return result; | |||
} | |||
return { | |||
setPosition: setPosition, | |||
getPosition: function () { return pos; }, | |||
scan: ignoreTrivia ? scanNextNonTrivia : scanNext, | |||
getToken: function () { return token; }, | |||
getTokenValue: function () { return value; }, | |||
getTokenOffset: function () { return tokenOffset; }, | |||
getTokenLength: function () { return pos - tokenOffset; }, | |||
getTokenError: function () { return scanError; } | |||
}; | |||
} | |||
function isWhiteSpace(ch) { | |||
return ch === 32 /* space */ || ch === 9 /* tab */ || ch === 11 /* verticalTab */ || ch === 12 /* formFeed */ || | |||
ch === 160 /* nonBreakingSpace */ || ch === 5760 /* ogham */ || ch >= 8192 /* enQuad */ && ch <= 8203 /* zeroWidthSpace */ || | |||
ch === 8239 /* narrowNoBreakSpace */ || ch === 8287 /* mathematicalSpace */ || ch === 12288 /* ideographicSpace */ || ch === 65279 /* byteOrderMark */; | |||
} | |||
function isLineBreak(ch) { | |||
return ch === 10 /* lineFeed */ || ch === 13 /* carriageReturn */ || ch === 8232 /* lineSeparator */ || ch === 8233 /* paragraphSeparator */; | |||
} | |||
function isDigit(ch) { | |||
return ch >= 48 /* _0 */ && ch <= 57 /* _9 */; | |||
} | |||
/** | |||
* Takes JSON with JavaScript-style comments and remove | |||
* them. Optionally replaces every none-newline character | |||
* of comments with a replaceCharacter | |||
*/ | |||
export function stripComments(text, replaceCh) { | |||
var _scanner = createScanner(text), parts = [], kind, offset = 0, pos; | |||
do { | |||
pos = _scanner.getPosition(); | |||
kind = _scanner.scan(); | |||
switch (kind) { | |||
case SyntaxKind.LineCommentTrivia: | |||
case SyntaxKind.BlockCommentTrivia: | |||
case SyntaxKind.EOF: | |||
if (offset !== pos) { | |||
parts.push(text.substring(offset, pos)); | |||
} | |||
if (replaceCh !== void 0) { | |||
parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh)); | |||
} | |||
offset = _scanner.getPosition(); | |||
break; | |||
} | |||
} while (kind !== SyntaxKind.EOF); | |||
return parts.join(''); | |||
} | |||
export var ParseErrorCode; | |||
(function (ParseErrorCode) { | |||
ParseErrorCode[ParseErrorCode["InvalidSymbol"] = 0] = "InvalidSymbol"; | |||
ParseErrorCode[ParseErrorCode["InvalidNumberFormat"] = 1] = "InvalidNumberFormat"; | |||
ParseErrorCode[ParseErrorCode["PropertyNameExpected"] = 2] = "PropertyNameExpected"; | |||
ParseErrorCode[ParseErrorCode["ValueExpected"] = 3] = "ValueExpected"; | |||
ParseErrorCode[ParseErrorCode["ColonExpected"] = 4] = "ColonExpected"; | |||
ParseErrorCode[ParseErrorCode["CommaExpected"] = 5] = "CommaExpected"; | |||
ParseErrorCode[ParseErrorCode["CloseBraceExpected"] = 6] = "CloseBraceExpected"; | |||
ParseErrorCode[ParseErrorCode["CloseBracketExpected"] = 7] = "CloseBracketExpected"; | |||
ParseErrorCode[ParseErrorCode["EndOfFileExpected"] = 8] = "EndOfFileExpected"; | |||
ParseErrorCode[ParseErrorCode["InvalidCommentToken"] = 9] = "InvalidCommentToken"; | |||
ParseErrorCode[ParseErrorCode["UnexpectedEndOfComment"] = 10] = "UnexpectedEndOfComment"; | |||
ParseErrorCode[ParseErrorCode["UnexpectedEndOfString"] = 11] = "UnexpectedEndOfString"; | |||
ParseErrorCode[ParseErrorCode["UnexpectedEndOfNumber"] = 12] = "UnexpectedEndOfNumber"; | |||
ParseErrorCode[ParseErrorCode["InvalidUnicode"] = 13] = "InvalidUnicode"; | |||
ParseErrorCode[ParseErrorCode["InvalidEscapeCharacter"] = 14] = "InvalidEscapeCharacter"; | |||
ParseErrorCode[ParseErrorCode["InvalidCharacter"] = 15] = "InvalidCharacter"; | |||
})(ParseErrorCode || (ParseErrorCode = {})); | |||
function getLiteralNodeType(value) { | |||
switch (typeof value) { | |||
case 'boolean': return 'boolean'; | |||
case 'number': return 'number'; | |||
case 'string': return 'string'; | |||
default: return 'null'; | |||
} | |||
} | |||
/** | |||
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index. | |||
*/ | |||
export function getLocation(text, position) { | |||
var segments = []; // strings or numbers | |||
var earlyReturnException = new Object(); | |||
var previousNode = void 0; | |||
var previousNodeInst = { | |||
value: {}, | |||
offset: 0, | |||
length: 0, | |||
type: 'object' | |||
}; | |||
var isAtPropertyKey = false; | |||
function setPreviousNode(value, offset, length, type) { | |||
previousNodeInst.value = value; | |||
previousNodeInst.offset = offset; | |||
previousNodeInst.length = length; | |||
previousNodeInst.type = type; | |||
previousNodeInst.columnOffset = void 0; | |||
previousNode = previousNodeInst; | |||
} | |||
try { | |||
visit(text, { | |||
onObjectBegin: function (offset, length) { | |||
if (position <= offset) { | |||
throw earlyReturnException; | |||
} | |||
previousNode = void 0; | |||
isAtPropertyKey = position > offset; | |||
segments.push(''); // push a placeholder (will be replaced) | |||
}, | |||
onObjectProperty: function (name, offset, length) { | |||
if (position < offset) { | |||
throw earlyReturnException; | |||
} | |||
setPreviousNode(name, offset, length, 'property'); | |||
segments[segments.length - 1] = name; | |||
if (position <= offset + length) { | |||
throw earlyReturnException; | |||
} | |||
}, | |||
onObjectEnd: function (offset, length) { | |||
if (position <= offset) { | |||
throw earlyReturnException; | |||
} | |||
previousNode = void 0; | |||
segments.pop(); | |||
}, | |||
onArrayBegin: function (offset, length) { | |||
if (position <= offset) { | |||
throw earlyReturnException; | |||
} | |||
previousNode = void 0; | |||
segments.push(0); | |||
}, | |||
onArrayEnd: function (offset, length) { | |||
if (position <= offset) { | |||
throw earlyReturnException; | |||
} | |||
previousNode = void 0; | |||
segments.pop(); | |||
}, | |||
onLiteralValue: function (value, offset, length) { | |||
if (position < offset) { | |||
throw earlyReturnException; | |||
} | |||
setPreviousNode(value, offset, length, getLiteralNodeType(value)); | |||
if (position <= offset + length) { | |||
throw earlyReturnException; | |||
} | |||
}, | |||
onSeparator: function (sep, offset, length) { | |||
if (position <= offset) { | |||
throw earlyReturnException; | |||
} | |||
if (sep === ':' && previousNode && previousNode.type === 'property') { | |||
previousNode.columnOffset = offset; | |||
isAtPropertyKey = false; | |||
previousNode = void 0; | |||
} | |||
else if (sep === ',') { | |||
var last = segments[segments.length - 1]; | |||
if (typeof last === 'number') { | |||
segments[segments.length - 1] = last + 1; | |||
} | |||
else { | |||
isAtPropertyKey = true; | |||
segments[segments.length - 1] = ''; | |||
} | |||
previousNode = void 0; | |||
} | |||
} | |||
}); | |||
} | |||
catch (e) { | |||
if (e !== earlyReturnException) { | |||
throw e; | |||
} | |||
} | |||
return { | |||
path: segments, | |||
previousNode: previousNode, | |||
isAtPropertyKey: isAtPropertyKey, | |||
matches: function (pattern) { | |||
var k = 0; | |||
for (var i = 0; k < pattern.length && i < segments.length; i++) { | |||
if (pattern[k] === segments[i] || pattern[k] === '*') { | |||
k++; | |||
} | |||
else if (pattern[k] !== '**') { | |||
return false; | |||
} | |||
} | |||
return k === pattern.length; | |||
} | |||
}; | |||
} | |||
/** | |||
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. | |||
* Therefore always check the errors list to find out if the input was valid. | |||
*/ | |||
export function parse(text, errors, options) { | |||
if (errors === void 0) { errors = []; } | |||
var currentProperty = null; | |||
var currentParent = []; | |||
var previousParents = []; | |||
function onValue(value) { | |||
if (Array.isArray(currentParent)) { | |||
currentParent.push(value); | |||
} | |||
else if (currentProperty) { | |||
currentParent[currentProperty] = value; | |||
} | |||
} | |||
var visitor = { | |||
onObjectBegin: function () { | |||
var object = {}; | |||
onValue(object); | |||
previousParents.push(currentParent); | |||
currentParent = object; | |||
currentProperty = null; | |||
}, | |||
onObjectProperty: function (name) { | |||
currentProperty = name; | |||
}, | |||
onObjectEnd: function () { | |||
currentParent = previousParents.pop(); | |||
}, | |||
onArrayBegin: function () { | |||
var array = []; | |||
onValue(array); | |||
previousParents.push(currentParent); | |||
currentParent = array; | |||
currentProperty = null; | |||
}, | |||
onArrayEnd: function () { | |||
currentParent = previousParents.pop(); | |||
}, | |||
onLiteralValue: onValue, | |||
onError: function (error, offset, length) { | |||
errors.push({ error: error, offset: offset, length: length }); | |||
} | |||
}; | |||
visit(text, visitor, options); | |||
return currentParent[0]; | |||
} | |||
/** | |||
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. | |||
*/ | |||
export function parseTree(text, errors, options) { | |||
if (errors === void 0) { errors = []; } | |||
var currentParent = { type: 'array', offset: -1, length: -1, children: [] }; // artificial root | |||
function ensurePropertyComplete(endOffset) { | |||
if (currentParent.type === 'property') { | |||
currentParent.length = endOffset - currentParent.offset; | |||
currentParent = currentParent.parent; | |||
} | |||
} | |||
function onValue(valueNode) { | |||
currentParent.children.push(valueNode); | |||
return valueNode; | |||
} | |||
var visitor = { | |||
onObjectBegin: function (offset) { | |||
currentParent = onValue({ type: 'object', offset: offset, length: -1, parent: currentParent, children: [] }); | |||
}, | |||
onObjectProperty: function (name, offset, length) { | |||
currentParent = onValue({ type: 'property', offset: offset, length: -1, parent: currentParent, children: [] }); | |||
currentParent.children.push({ type: 'string', value: name, offset: offset, length: length, parent: currentParent }); | |||
}, | |||
onObjectEnd: function (offset, length) { | |||
currentParent.length = offset + length - currentParent.offset; | |||
currentParent = currentParent.parent; | |||
ensurePropertyComplete(offset + length); | |||
}, | |||
onArrayBegin: function (offset, length) { | |||
currentParent = onValue({ type: 'array', offset: offset, length: -1, parent: currentParent, children: [] }); | |||
}, | |||
onArrayEnd: function (offset, length) { | |||
currentParent.length = offset + length - currentParent.offset; | |||
currentParent = currentParent.parent; | |||
ensurePropertyComplete(offset + length); | |||
}, | |||
onLiteralValue: function (value, offset, length) { | |||
onValue({ type: getLiteralNodeType(value), offset: offset, length: length, parent: currentParent, value: value }); | |||
ensurePropertyComplete(offset + length); | |||
}, | |||
onSeparator: function (sep, offset, length) { | |||
if (currentParent.type === 'property') { | |||
if (sep === ':') { | |||
currentParent.columnOffset = offset; | |||
} | |||
else if (sep === ',') { | |||
ensurePropertyComplete(offset); | |||
} | |||
} | |||
}, | |||
onError: function (error, offset, length) { | |||
errors.push({ error: error, offset: offset, length: length }); | |||
} | |||
}; | |||
visit(text, visitor, options); | |||
var result = currentParent.children[0]; | |||
if (result) { | |||
delete result.parent; | |||
} | |||
return result; | |||
} | |||
/** | |||
* Finds the node at the given path in a JSON DOM. | |||
*/ | |||
export function findNodeAtLocation(root, path) { | |||
if (!root) { | |||
return void 0; | |||
} | |||
var node = root; | |||
for (var _i = 0, path_1 = path; _i < path_1.length; _i++) { | |||
var segment = path_1[_i]; | |||
if (typeof segment === 'string') { | |||
if (node.type !== 'object' || !Array.isArray(node.children)) { | |||
return void 0; | |||
} | |||
var found = false; | |||
for (var _a = 0, _b = node.children; _a < _b.length; _a++) { | |||
var propertyNode = _b[_a]; | |||
if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment) { | |||
node = propertyNode.children[1]; | |||
found = true; | |||
break; | |||
} | |||
} | |||
if (!found) { | |||
return void 0; | |||
} | |||
} | |||
else { | |||
var index = segment; | |||
if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) { | |||
return void 0; | |||
} | |||
node = node.children[index]; | |||
} | |||
} | |||
return node; | |||
} | |||
/** | |||
* Evaluates the JavaScript object of the given JSON DOM node | |||
*/ | |||
export function getNodeValue(node) { | |||
if (node.type === 'array') { | |||
return node.children.map(getNodeValue); | |||
} | |||
else if (node.type === 'object') { | |||
var obj = Object.create(null); | |||
for (var _i = 0, _a = node.children; _i < _a.length; _i++) { | |||
var prop = _a[_i]; | |||
obj[prop.children[0].value] = getNodeValue(prop.children[1]); | |||
} | |||
return obj; | |||
} | |||
return node.value; | |||
} | |||
/** | |||
* Parses the given text and invokes the visitor functions for each object, array and literal reached. | |||
*/ | |||
export function visit(text, visitor, options) { | |||
var _scanner = createScanner(text, false); | |||
function toNoArgVisit(visitFunction) { | |||
return visitFunction ? function () { return visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength()); } : function () { return true; }; | |||
} | |||
function toOneArgVisit(visitFunction) { | |||
return visitFunction ? function (arg) { return visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength()); } : function () { return true; }; | |||
} | |||
var onObjectBegin = toNoArgVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisit(visitor.onObjectProperty), onObjectEnd = toNoArgVisit(visitor.onObjectEnd), onArrayBegin = toNoArgVisit(visitor.onArrayBegin), onArrayEnd = toNoArgVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisit(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError); | |||
var disallowComments = options && options.disallowComments; | |||
var allowTrailingComma = options && options.allowTrailingComma; | |||
function scanNext() { | |||
while (true) { | |||
var token = _scanner.scan(); | |||
switch (_scanner.getTokenError()) { | |||
case ScanError.InvalidUnicode: | |||
handleError(ParseErrorCode.InvalidUnicode); | |||
break; | |||
case ScanError.InvalidEscapeCharacter: | |||
handleError(ParseErrorCode.InvalidEscapeCharacter); | |||
break; | |||
case ScanError.UnexpectedEndOfNumber: | |||
handleError(ParseErrorCode.UnexpectedEndOfNumber); | |||
break; | |||
case ScanError.UnexpectedEndOfComment: | |||
if (!disallowComments) { | |||
handleError(ParseErrorCode.UnexpectedEndOfComment); | |||
} | |||
break; | |||
case ScanError.UnexpectedEndOfString: | |||
handleError(ParseErrorCode.UnexpectedEndOfString); | |||
break; | |||
case ScanError.InvalidCharacter: | |||
handleError(ParseErrorCode.InvalidCharacter); | |||
break; | |||
} | |||
switch (token) { | |||
case SyntaxKind.LineCommentTrivia: | |||
case SyntaxKind.BlockCommentTrivia: | |||
if (disallowComments) { | |||
handleError(ParseErrorCode.InvalidCommentToken); | |||
} | |||
else { | |||
onComment(); | |||
} | |||
break; | |||
case SyntaxKind.Unknown: | |||
handleError(ParseErrorCode.InvalidSymbol); | |||
break; | |||
case SyntaxKind.Trivia: | |||
case SyntaxKind.LineBreakTrivia: | |||
break; | |||
default: | |||
return token; | |||
} | |||
} | |||
} | |||
function handleError(error, skipUntilAfter, skipUntil) { | |||
if (skipUntilAfter === void 0) { skipUntilAfter = []; } | |||
if (skipUntil === void 0) { skipUntil = []; } | |||
onError(error); | |||
if (skipUntilAfter.length + skipUntil.length > 0) { | |||
var token = _scanner.getToken(); | |||
while (token !== SyntaxKind.EOF) { | |||
if (skipUntilAfter.indexOf(token) !== -1) { | |||
scanNext(); | |||
break; | |||
} | |||
else if (skipUntil.indexOf(token) !== -1) { | |||
break; | |||
} | |||
token = scanNext(); | |||
} | |||
} | |||
} | |||
function parseString(isValue) { | |||
var value = _scanner.getTokenValue(); | |||
if (isValue) { | |||
onLiteralValue(value); | |||
} | |||
else { | |||
onObjectProperty(value); | |||
} | |||
scanNext(); | |||
return true; | |||
} | |||
function parseLiteral() { | |||
switch (_scanner.getToken()) { | |||
case SyntaxKind.NumericLiteral: | |||
var value = 0; | |||
try { | |||
value = JSON.parse(_scanner.getTokenValue()); | |||
if (typeof value !== 'number') { | |||
handleError(ParseErrorCode.InvalidNumberFormat); | |||
value = 0; | |||
} | |||
} | |||
catch (e) { | |||
handleError(ParseErrorCode.InvalidNumberFormat); | |||
} | |||
onLiteralValue(value); | |||
break; | |||
case SyntaxKind.NullKeyword: | |||
onLiteralValue(null); | |||
break; | |||
case SyntaxKind.TrueKeyword: | |||
onLiteralValue(true); | |||
break; | |||
case SyntaxKind.FalseKeyword: | |||
onLiteralValue(false); | |||
break; | |||
default: | |||
return false; | |||
} | |||
scanNext(); | |||
return true; | |||
} | |||
function parseProperty() { | |||
if (_scanner.getToken() !== SyntaxKind.StringLiteral) { | |||
handleError(ParseErrorCode.PropertyNameExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken]); | |||
return false; | |||
} | |||
parseString(false); | |||
if (_scanner.getToken() === SyntaxKind.ColonToken) { | |||
onSeparator(':'); | |||
scanNext(); // consume colon | |||
if (!parseValue()) { | |||
handleError(ParseErrorCode.ValueExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken]); | |||
} | |||
} | |||
else { | |||
handleError(ParseErrorCode.ColonExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken]); | |||
} | |||
return true; | |||
} | |||
function parseObject() { | |||
onObjectBegin(); | |||
scanNext(); // consume open brace | |||
var needsComma = false; | |||
while (_scanner.getToken() !== SyntaxKind.CloseBraceToken && _scanner.getToken() !== SyntaxKind.EOF) { | |||
if (_scanner.getToken() === SyntaxKind.CommaToken) { | |||
if (!needsComma) { | |||
handleError(ParseErrorCode.ValueExpected, [], []); | |||
} | |||
onSeparator(','); | |||
scanNext(); // consume comma | |||
if (_scanner.getToken() === SyntaxKind.CloseBraceToken && allowTrailingComma) { | |||
break; | |||
} | |||
} | |||
else if (needsComma) { | |||
handleError(ParseErrorCode.CommaExpected, [], []); | |||
} | |||
if (!parseProperty()) { | |||
handleError(ParseErrorCode.ValueExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken]); | |||
} | |||
needsComma = true; | |||
} | |||
onObjectEnd(); | |||
if (_scanner.getToken() !== SyntaxKind.CloseBraceToken) { | |||
handleError(ParseErrorCode.CloseBraceExpected, [SyntaxKind.CloseBraceToken], []); | |||
} | |||
else { | |||
scanNext(); // consume close brace | |||
} | |||
return true; | |||
} | |||
function parseArray() { | |||
onArrayBegin(); | |||
scanNext(); // consume open bracket | |||
var needsComma = false; | |||
while (_scanner.getToken() !== SyntaxKind.CloseBracketToken && _scanner.getToken() !== SyntaxKind.EOF) { | |||
if (_scanner.getToken() === SyntaxKind.CommaToken) { | |||
if (!needsComma) { | |||
handleError(ParseErrorCode.ValueExpected, [], []); | |||
} | |||
onSeparator(','); | |||
scanNext(); // consume comma | |||
if (_scanner.getToken() === SyntaxKind.CloseBracketToken && allowTrailingComma) { | |||
break; | |||
} | |||
} | |||
else if (needsComma) { | |||
handleError(ParseErrorCode.CommaExpected, [], []); | |||
} | |||
if (!parseValue()) { | |||
handleError(ParseErrorCode.ValueExpected, [], [SyntaxKind.CloseBracketToken, SyntaxKind.CommaToken]); | |||
} | |||
needsComma = true; | |||
} | |||
onArrayEnd(); | |||
if (_scanner.getToken() !== SyntaxKind.CloseBracketToken) { | |||
handleError(ParseErrorCode.CloseBracketExpected, [SyntaxKind.CloseBracketToken], []); | |||
} | |||
else { | |||
scanNext(); // consume close bracket | |||
} | |||
return true; | |||
} | |||
function parseValue() { | |||
switch (_scanner.getToken()) { | |||
case SyntaxKind.OpenBracketToken: | |||
return parseArray(); | |||
case SyntaxKind.OpenBraceToken: | |||
return parseObject(); | |||
case SyntaxKind.StringLiteral: | |||
return parseString(true); | |||
default: | |||
return parseLiteral(); | |||
} | |||
} | |||
scanNext(); | |||
if (_scanner.getToken() === SyntaxKind.EOF) { | |||
return true; | |||
} | |||
if (!parseValue()) { | |||
handleError(ParseErrorCode.ValueExpected, [], []); | |||
return false; | |||
} | |||
if (_scanner.getToken() !== SyntaxKind.EOF) { | |||
handleError(ParseErrorCode.EndOfFileExpected, [], []); | |||
} | |||
return true; | |||
} | |||
/** | |||
* Computes the edits needed to format a JSON document. | |||
* | |||
* @param documentText The input text | |||
* @param range The range to format or `undefined` to format the full content | |||
* @param options The formatting options | |||
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or | |||
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of | |||
* text in the original document. However, multiple edits can have | |||
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first. | |||
* To apply edits to an input, you can use `applyEdits` | |||
*/ | |||
export function format(documentText, range, options) { | |||
return _format(documentText, range, options); | |||
} | |||
/** | |||
* Computes the edits needed to modify a value in the JSON document. | |||
* | |||
* @param documentText The input text | |||
* @param path The path of the value to change. The path represents either to the document root, a property or an array item. | |||
* If the path points to an non-existing property or item, it will be created. | |||
* @param value The new value for the specified property or item. If the value is undefined, | |||
* the property or item will be removed. | |||
* @param options Options | |||
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or | |||
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of | |||
* text in the original document. However, multiple edits can have | |||
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first. | |||
* To apply edits to an input, you can use `applyEdits` | |||
*/ | |||
export function modify(text, path, value, options) { | |||
return setProperty(text, path, value, options.formattingOptions, options.getInsertionIndex); | |||
} | |||
/** | |||
* Applies edits to a input string. | |||
*/ | |||
export function applyEdits(text, edits) { | |||
for (var i = edits.length - 1; i >= 0; i--) { | |||
text = applyEdit(text, edits[i]); | |||
} | |||
return text; | |||
} | |||
//# sourceMappingURL=main.js.map |
@@ -1,5 +0,0 @@ | |||
import { Edit, FormattingOptions, JSONPath } from './main'; | |||
export declare function removeProperty(text: string, path: JSONPath, formattingOptions: FormattingOptions): Edit[]; | |||
export declare function setProperty(text: string, path: JSONPath, value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number): Edit[]; | |||
export declare function applyEdit(text: string, edit: Edit): string; | |||
export declare function isWS(text: string, offset: number): boolean; |
@@ -1,183 +0,0 @@ | |||
(function (factory) { | |||
if (typeof module === "object" && typeof module.exports === "object") { | |||
var v = factory(require, exports); | |||
if (v !== undefined) module.exports = v; | |||
} | |||
else if (typeof define === "function" && define.amd) { | |||
define(["require", "exports", "./main", "./format"], factory); | |||
} | |||
})(function (require, exports) { | |||
/*--------------------------------------------------------------------------------------------- | |||
* Copyright (c) Microsoft Corporation. All rights reserved. | |||
* Licensed under the MIT License. See License.txt in the project root for license information. | |||
*--------------------------------------------------------------------------------------------*/ | |||
'use strict'; | |||
Object.defineProperty(exports, "__esModule", { value: true }); | |||
var main_1 = require("./main"); | |||
var format_1 = require("./format"); | |||
function removeProperty(text, path, formattingOptions) { | |||
return setProperty(text, path, void 0, formattingOptions); | |||
} | |||
exports.removeProperty = removeProperty; | |||
function setProperty(text, path, value, formattingOptions, getInsertionIndex) { | |||
var errors = []; | |||
var root = main_1.parseTree(text, errors); | |||
var parent = void 0; | |||
var lastSegment = void 0; | |||
while (path.length > 0) { | |||
lastSegment = path.pop(); | |||
parent = main_1.findNodeAtLocation(root, path); | |||
if (parent === void 0 && value !== void 0) { | |||
if (typeof lastSegment === 'string') { | |||
value = (_a = {}, _a[lastSegment] = value, _a); | |||
} | |||
else { | |||
value = [value]; | |||
} | |||
} | |||
else { | |||
break; | |||
} | |||
} | |||
if (!parent) { | |||
// empty document | |||
if (value === void 0) { | |||
throw new Error('Can not delete in empty document'); | |||
} | |||
return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, formattingOptions); | |||
} | |||
else if (parent.type === 'object' && typeof lastSegment === 'string' && Array.isArray(parent.children)) { | |||
var existing = main_1.findNodeAtLocation(parent, [lastSegment]); | |||
if (existing !== void 0) { | |||
if (value === void 0) { | |||
if (!existing.parent) { | |||
throw new Error('Malformed AST'); | |||
} | |||
var propertyIndex = parent.children.indexOf(existing.parent); | |||
var removeBegin = void 0; | |||
var removeEnd = existing.parent.offset + existing.parent.length; | |||
if (propertyIndex > 0) { | |||
// remove the comma of the previous node | |||
var previous = parent.children[propertyIndex - 1]; | |||
removeBegin = previous.offset + previous.length; | |||
} | |||
else { | |||
removeBegin = parent.offset + 1; | |||
if (parent.children.length > 1) { | |||
// remove the comma of the next node | |||
var next = parent.children[1]; | |||
removeEnd = next.offset; | |||
} | |||
} | |||
return withFormatting(text, { offset: removeBegin, length: removeEnd - removeBegin, content: '' }, formattingOptions); | |||
} | |||
else { | |||
// set value of existing property | |||
return withFormatting(text, { offset: existing.offset, length: existing.length, content: JSON.stringify(value) }, formattingOptions); | |||
} | |||
} | |||
else { | |||
if (value === void 0) { | |||
return []; // property does not exist, nothing to do | |||
} | |||
var newProperty = JSON.stringify(lastSegment) + ": " + JSON.stringify(value); | |||
var index = getInsertionIndex ? getInsertionIndex(parent.children.map(function (p) { return p.children[0].value; })) : parent.children.length; | |||
var edit = void 0; | |||
if (index > 0) { | |||
var previous = parent.children[index - 1]; | |||
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty }; | |||
} | |||
else if (parent.children.length === 0) { | |||
edit = { offset: parent.offset + 1, length: 0, content: newProperty }; | |||
} | |||
else { | |||
edit = { offset: parent.offset + 1, length: 0, content: newProperty + ',' }; | |||
} | |||
return withFormatting(text, edit, formattingOptions); | |||
} | |||
} | |||
else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) { | |||
var insertIndex = lastSegment; | |||
if (insertIndex === -1) { | |||
// Insert | |||
var newProperty = "" + JSON.stringify(value); | |||
var edit = void 0; | |||
if (parent.children.length === 0) { | |||
edit = { offset: parent.offset + 1, length: 0, content: newProperty }; | |||
} | |||
else { | |||
var previous = parent.children[parent.children.length - 1]; | |||
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty }; | |||
} | |||
return withFormatting(text, edit, formattingOptions); | |||
} | |||
else { | |||
if (value === void 0 && parent.children.length >= 0) { | |||
//Removal | |||
var removalIndex = lastSegment; | |||
var toRemove = parent.children[removalIndex]; | |||
var edit = void 0; | |||
if (parent.children.length === 1) { | |||
// only item | |||
edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' }; | |||
} | |||
else if (parent.children.length - 1 === removalIndex) { | |||
// last item | |||
var previous = parent.children[removalIndex - 1]; | |||
var offset = previous.offset + previous.length; | |||
var parentEndOffset = parent.offset + parent.length; | |||
edit = { offset: offset, length: parentEndOffset - 2 - offset, content: '' }; | |||
} | |||
else { | |||
edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' }; | |||
} | |||
return withFormatting(text, edit, formattingOptions); | |||
} | |||
else { | |||
throw new Error('Array modification not supported yet'); | |||
} | |||
} | |||
} | |||
else { | |||
throw new Error("Can not add " + (typeof lastSegment !== 'number' ? 'index' : 'property') + " to parent of type " + parent.type); | |||
} | |||
var _a; | |||
} | |||
exports.setProperty = setProperty; | |||
function withFormatting(text, edit, formattingOptions) { | |||
// apply the edit | |||
var newText = applyEdit(text, edit); | |||
// format the new text | |||
var begin = edit.offset; | |||
var end = edit.offset + edit.content.length; | |||
if (edit.length === 0 || edit.content.length === 0) { | |||
while (begin > 0 && !format_1.isEOL(newText, begin - 1)) { | |||
begin--; | |||
} | |||
while (end < newText.length && !format_1.isEOL(newText, end)) { | |||
end++; | |||
} | |||
} | |||
var edits = format_1.format(newText, { offset: begin, length: end - begin }, formattingOptions); | |||
// apply the formatting edits and track the begin and end offsets of the changes | |||
for (var i = edits.length - 1; i >= 0; i--) { | |||
var edit_1 = edits[i]; | |||
newText = applyEdit(newText, edit_1); | |||
begin = Math.min(begin, edit_1.offset); | |||
end = Math.max(end, edit_1.offset + edit_1.length); | |||
end += edit_1.content.length - edit_1.length; | |||
} | |||
// create a single edit with all changes | |||
var editLength = text.length - (newText.length - end) - begin; | |||
return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }]; | |||
} | |||
function applyEdit(text, edit) { | |||
return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length); | |||
} | |||
exports.applyEdit = applyEdit; | |||
function isWS(text, offset) { | |||
return '\r\n \t'.indexOf(text.charAt(offset)) !== -1; | |||
} | |||
exports.isWS = isWS; | |||
}); | |||
//# sourceMappingURL=edit.js.map |
@@ -1,3 +0,0 @@ | |||
import * as Json from './main'; | |||
export declare function format(documentText: string, range: Json.Range | undefined, options: Json.FormattingOptions): Json.Edit[]; | |||
export declare function isEOL(text: string, offset: number): boolean; |
@@ -1,208 +0,0 @@ | |||
(function (factory) { | |||
if (typeof module === "object" && typeof module.exports === "object") { | |||
var v = factory(require, exports); | |||
if (v !== undefined) module.exports = v; | |||
} | |||
else if (typeof define === "function" && define.amd) { | |||
define(["require", "exports", "./main"], factory); | |||
} | |||
})(function (require, exports) { | |||
/*--------------------------------------------------------------------------------------------- | |||
* Copyright (c) Microsoft Corporation. All rights reserved. | |||
* Licensed under the MIT License. See License.txt in the project root for license information. | |||
*--------------------------------------------------------------------------------------------*/ | |||
'use strict'; | |||
Object.defineProperty(exports, "__esModule", { value: true }); | |||
var Json = require("./main"); | |||
function format(documentText, range, options) { | |||
var initialIndentLevel; | |||
var formatText; | |||
var formatTextStart; | |||
var rangeStart; | |||
var rangeEnd; | |||
if (range) { | |||
rangeStart = range.offset; | |||
rangeEnd = rangeStart + range.length; | |||
formatTextStart = rangeStart; | |||
while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) { | |||
formatTextStart--; | |||
} | |||
var endOffset = rangeEnd; | |||
while (endOffset < documentText.length && !isEOL(documentText, endOffset)) { | |||
endOffset++; | |||
} | |||
formatText = documentText.substring(formatTextStart, endOffset); | |||
initialIndentLevel = computeIndentLevel(formatText, 0, options); | |||
} | |||
else { | |||
formatText = documentText; | |||
initialIndentLevel = 0; | |||
formatTextStart = 0; | |||
rangeStart = 0; | |||
rangeEnd = documentText.length; | |||
} | |||
var eol = getEOL(options, documentText); | |||
var lineBreak = false; | |||
var indentLevel = 0; | |||
var indentValue; | |||
if (options.insertSpaces) { | |||
indentValue = repeat(' ', options.tabSize || 4); | |||
} | |||
else { | |||
indentValue = '\t'; | |||
} | |||
var scanner = Json.createScanner(formatText, false); | |||
var hasError = false; | |||
function newLineAndIndent() { | |||
return eol + repeat(indentValue, initialIndentLevel + indentLevel); | |||
} | |||
function scanNext() { | |||
var token = scanner.scan(); | |||
lineBreak = false; | |||
while (token === Json.SyntaxKind.Trivia || token === Json.SyntaxKind.LineBreakTrivia) { | |||
lineBreak = lineBreak || (token === Json.SyntaxKind.LineBreakTrivia); | |||
token = scanner.scan(); | |||
} | |||
hasError = token === Json.SyntaxKind.Unknown || scanner.getTokenError() !== Json.ScanError.None; | |||
return token; | |||
} | |||
var editOperations = []; | |||
function addEdit(text, startOffset, endOffset) { | |||
if (!hasError && startOffset < rangeEnd && endOffset > rangeStart && documentText.substring(startOffset, endOffset) !== text) { | |||
editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text }); | |||
} | |||
} | |||
var firstToken = scanNext(); | |||
if (firstToken !== Json.SyntaxKind.EOF) { | |||
var firstTokenStart = scanner.getTokenOffset() + formatTextStart; | |||
var initialIndent = repeat(indentValue, initialIndentLevel); | |||
addEdit(initialIndent, formatTextStart, firstTokenStart); | |||
} | |||
while (firstToken !== Json.SyntaxKind.EOF) { | |||
var firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart; | |||
var secondToken = scanNext(); | |||
var replaceContent = ''; | |||
while (!lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) { | |||
// comments on the same line: keep them on the same line, but ignore them otherwise | |||
var commentTokenStart = scanner.getTokenOffset() + formatTextStart; | |||
addEdit(' ', firstTokenEnd, commentTokenStart); | |||
firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart; | |||
replaceContent = secondToken === Json.SyntaxKind.LineCommentTrivia ? newLineAndIndent() : ''; | |||
secondToken = scanNext(); | |||
} | |||
if (secondToken === Json.SyntaxKind.CloseBraceToken) { | |||
if (firstToken !== Json.SyntaxKind.OpenBraceToken) { | |||
indentLevel--; | |||
replaceContent = newLineAndIndent(); | |||
} | |||
} | |||
else if (secondToken === Json.SyntaxKind.CloseBracketToken) { | |||
if (firstToken !== Json.SyntaxKind.OpenBracketToken) { | |||
indentLevel--; | |||
replaceContent = newLineAndIndent(); | |||
} | |||
} | |||
else { | |||
switch (firstToken) { | |||
case Json.SyntaxKind.OpenBracketToken: | |||
case Json.SyntaxKind.OpenBraceToken: | |||
indentLevel++; | |||
replaceContent = newLineAndIndent(); | |||
break; | |||
case Json.SyntaxKind.CommaToken: | |||
case Json.SyntaxKind.LineCommentTrivia: | |||
replaceContent = newLineAndIndent(); | |||
break; | |||
case Json.SyntaxKind.BlockCommentTrivia: | |||
if (lineBreak) { | |||
replaceContent = newLineAndIndent(); | |||
} | |||
else { | |||
// symbol following comment on the same line: keep on same line, separate with ' ' | |||
replaceContent = ' '; | |||
} | |||
break; | |||
case Json.SyntaxKind.ColonToken: | |||
replaceContent = ' '; | |||
break; | |||
case Json.SyntaxKind.StringLiteral: | |||
if (secondToken === Json.SyntaxKind.ColonToken) { | |||
replaceContent = ''; | |||
break; | |||
} | |||
// fall through | |||
case Json.SyntaxKind.NullKeyword: | |||
case Json.SyntaxKind.TrueKeyword: | |||
case Json.SyntaxKind.FalseKeyword: | |||
case Json.SyntaxKind.NumericLiteral: | |||
case Json.SyntaxKind.CloseBraceToken: | |||
case Json.SyntaxKind.CloseBracketToken: | |||
if (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia) { | |||
replaceContent = ' '; | |||
} | |||
else if (secondToken !== Json.SyntaxKind.CommaToken && secondToken !== Json.SyntaxKind.EOF) { | |||
hasError = true; | |||
} | |||
break; | |||
case Json.SyntaxKind.Unknown: | |||
hasError = true; | |||
break; | |||
} | |||
if (lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) { | |||
replaceContent = newLineAndIndent(); | |||
} | |||
} | |||
var secondTokenStart = scanner.getTokenOffset() + formatTextStart; | |||
addEdit(replaceContent, firstTokenEnd, secondTokenStart); | |||
firstToken = secondToken; | |||
} | |||
return editOperations; | |||
} | |||
exports.format = format; | |||
function repeat(s, count) { | |||
var result = ''; | |||
for (var i = 0; i < count; i++) { | |||
result += s; | |||
} | |||
return result; | |||
} | |||
function computeIndentLevel(content, offset, options) { | |||
var i = 0; | |||
var nChars = 0; | |||
var tabSize = options.tabSize || 4; | |||
while (i < content.length) { | |||
var ch = content.charAt(i); | |||
if (ch === ' ') { | |||
nChars++; | |||
} | |||
else if (ch === '\t') { | |||
nChars += tabSize; | |||
} | |||
else { | |||
break; | |||
} | |||
i++; | |||
} | |||
return Math.floor(nChars / tabSize); | |||
} | |||
function getEOL(options, text) { | |||
for (var i = 0; i < text.length; i++) { | |||
var ch = text.charAt(i); | |||
if (ch === '\r') { | |||
if (i + 1 < text.length && text.charAt(i + 1) === '\n') { | |||
return '\r\n'; | |||
} | |||
return '\r'; | |||
} | |||
else if (ch === '\n') { | |||
return '\n'; | |||
} | |||
} | |||
return (options && options.eol) || '\n'; | |||
} | |||
function isEOL(text, offset) { | |||
return '\r\n'.indexOf(text.charAt(offset)) !== -1; | |||
} | |||
exports.isEOL = isEOL; | |||
}); | |||
//# sourceMappingURL=format.js.map |
@@ -1,289 +0,0 @@ | |||
export declare enum ScanError { | |||
None = 0, | |||
UnexpectedEndOfComment = 1, | |||
UnexpectedEndOfString = 2, | |||
UnexpectedEndOfNumber = 3, | |||
InvalidUnicode = 4, | |||
InvalidEscapeCharacter = 5, | |||
InvalidCharacter = 6, | |||
} | |||
export declare enum SyntaxKind { | |||
Unknown = 0, | |||
OpenBraceToken = 1, | |||
CloseBraceToken = 2, | |||
OpenBracketToken = 3, | |||
CloseBracketToken = 4, | |||
CommaToken = 5, | |||
ColonToken = 6, | |||
NullKeyword = 7, | |||
TrueKeyword = 8, | |||
FalseKeyword = 9, | |||
StringLiteral = 10, | |||
NumericLiteral = 11, | |||
LineCommentTrivia = 12, | |||
BlockCommentTrivia = 13, | |||
LineBreakTrivia = 14, | |||
Trivia = 15, | |||
EOF = 16, | |||
} | |||
/** | |||
* The scanner object, representing a JSON scanner at a position in the input string. | |||
*/ | |||
export interface JSONScanner { | |||
/** | |||
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token. | |||
*/ | |||
setPosition(pos: number): void; | |||
/** | |||
* Read the next token. Returns the tolen code. | |||
*/ | |||
scan(): SyntaxKind; | |||
/** | |||
* Returns the current scan position, which is after the last read token. | |||
*/ | |||
getPosition(): number; | |||
/** | |||
* Returns the last read token. | |||
*/ | |||
getToken(): SyntaxKind; | |||
/** | |||
* Returns the last read token value. The value for strings is the decoded string content. For numbers its of type number, for boolean it's true or false. | |||
*/ | |||
getTokenValue(): string; | |||
/** | |||
* The start offset of the last read token. | |||
*/ | |||
getTokenOffset(): number; | |||
/** | |||
* The length of the last read token. | |||
*/ | |||
getTokenLength(): number; | |||
/** | |||
* An error code of the last scan. | |||
*/ | |||
getTokenError(): ScanError; | |||
} | |||
/** | |||
* Creates a JSON scanner on the given text. | |||
* If ignoreTrivia is set, whitespaces or comments are ignored. | |||
*/ | |||
export declare function createScanner(text: string, ignoreTrivia?: boolean): JSONScanner; | |||
/** | |||
* Takes JSON with JavaScript-style comments and remove | |||
* them. Optionally replaces every none-newline character | |||
* of comments with a replaceCharacter | |||
*/ | |||
export declare function stripComments(text: string, replaceCh?: string): string; | |||
export interface ParseError { | |||
error: ParseErrorCode; | |||
offset: number; | |||
length: number; | |||
} | |||
export declare enum ParseErrorCode { | |||
InvalidSymbol = 0, | |||
InvalidNumberFormat = 1, | |||
PropertyNameExpected = 2, | |||
ValueExpected = 3, | |||
ColonExpected = 4, | |||
CommaExpected = 5, | |||
CloseBraceExpected = 6, | |||
CloseBracketExpected = 7, | |||
EndOfFileExpected = 8, | |||
InvalidCommentToken = 9, | |||
UnexpectedEndOfComment = 10, | |||
UnexpectedEndOfString = 11, | |||
UnexpectedEndOfNumber = 12, | |||
InvalidUnicode = 13, | |||
InvalidEscapeCharacter = 14, | |||
InvalidCharacter = 15, | |||
} | |||
export declare type NodeType = 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null'; | |||
export interface Node { | |||
type: NodeType; | |||
value?: any; | |||
offset: number; | |||
length: number; | |||
columnOffset?: number; | |||
parent?: Node; | |||
children?: Node[]; | |||
} | |||
export declare type Segment = string | number; | |||
export declare type JSONPath = Segment[]; | |||
export interface Location { | |||
/** | |||
* The previous property key or literal value (string, number, boolean or null) or undefined. | |||
*/ | |||
previousNode?: Node; | |||
/** | |||
* The path describing the location in the JSON document. The path consists of a sequence strings | |||
* representing an object property or numbers for array indices. | |||
*/ | |||
path: JSONPath; | |||
/** | |||
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices). | |||
* '*' will match a single segment, of any property name or index. | |||
* '**' will match a sequece of segments or no segment, of any property name or index. | |||
*/ | |||
matches: (patterns: JSONPath) => boolean; | |||
/** | |||
* If set, the location's offset is at a property key. | |||
*/ | |||
isAtPropertyKey: boolean; | |||
} | |||
/** | |||
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index. | |||
*/ | |||
export declare function getLocation(text: string, position: number): Location; | |||
export interface ParseOptions { | |||
disallowComments?: boolean; | |||
allowTrailingComma?: boolean; | |||
} | |||
/** | |||
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. | |||
* Therefore always check the errors list to find out if the input was valid. | |||
*/ | |||
export declare function parse(text: string, errors?: ParseError[], options?: ParseOptions): any; | |||
/** | |||
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. | |||
*/ | |||
export declare function parseTree(text: string, errors?: ParseError[], options?: ParseOptions): Node; | |||
/** | |||
* Finds the node at the given path in a JSON DOM. | |||
*/ | |||
export declare function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined; | |||
/** | |||
* Evaluates the JavaScript object of the given JSON DOM node | |||
*/ | |||
export declare function getNodeValue(node: Node): any; | |||
/** | |||
* Parses the given text and invokes the visitor functions for each object, array and literal reached. | |||
*/ | |||
export declare function visit(text: string, visitor: JSONVisitor, options?: ParseOptions): any; | |||
export interface JSONVisitor { | |||
/** | |||
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace. | |||
*/ | |||
onObjectBegin?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked when a property is encountered. The offset and length represent the location of the property name. | |||
*/ | |||
onObjectProperty?: (property: string, offset: number, length: number) => void; | |||
/** | |||
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace. | |||
*/ | |||
onObjectEnd?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket. | |||
*/ | |||
onArrayBegin?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket. | |||
*/ | |||
onArrayEnd?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value. | |||
*/ | |||
onLiteralValue?: (value: any, offset: number, length: number) => void; | |||
/** | |||
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator. | |||
*/ | |||
onSeparator?: (character: string, offset: number, length: number) => void; | |||
/** | |||
* When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment. | |||
*/ | |||
onComment?: (offset: number, length: number) => void; | |||
/** | |||
* Invoked on an error. | |||
*/ | |||
onError?: (error: ParseErrorCode, offset: number, length: number) => void; | |||
} | |||
/** | |||
* Represents a text modification | |||
*/ | |||
export interface Edit { | |||
/** | |||
* The start offset of the modification. | |||
*/ | |||
offset: number; | |||
/** | |||
* The length of the modification. Must not be negative. Empty length represents an *insert*. | |||
*/ | |||
length: number; | |||
/** | |||
* The new content. Empty content represents a *remove*. | |||
*/ | |||
content: string; | |||
} | |||
/** | |||
* A text range in the document | |||
*/ | |||
export interface Range { | |||
/** | |||
* The start offset of the range. | |||
*/ | |||
offset: number; | |||
/** | |||
* The length of the range. Must not be negative. | |||
*/ | |||
length: number; | |||
} | |||
export interface FormattingOptions { | |||
/** | |||
* If indentation is based on spaces (`insertSpaces` = true), then what is the number of spaces that make an indent? | |||
*/ | |||
tabSize?: number; | |||
/** | |||
* Is indentation based on spaces? | |||
*/ | |||
insertSpaces?: boolean; | |||
/** | |||
* The default 'end of line' character. If not set, '\n' is used as default. | |||
*/ | |||
eol?: string; | |||
} | |||
/** | |||
* Computes the edits needed to format a JSON document. | |||
* | |||
* @param documentText The input text | |||
* @param range The range to format or `undefined` to format the full content | |||
* @param options The formatting options | |||
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or | |||
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of | |||
* text in the original document. However, multiple edits can have | |||
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first. | |||
* To apply edits to an input, you can use `applyEdits` | |||
*/ | |||
export declare function format(documentText: string, range: Range | undefined, options: FormattingOptions): Edit[]; | |||
/** | |||
* Options used when computing the modification edits | |||
*/ | |||
export interface ModificationOptions { | |||
/** | |||
* Formatting options | |||
*/ | |||
formattingOptions: FormattingOptions; | |||
/** | |||
* Optional fucntion to define the insertion index given an existing list of properties. | |||
*/ | |||
getInsertionIndex?: (properties: string[]) => number; | |||
} | |||
/** | |||
* Computes the edits needed to modify a value in the JSON document. | |||
* | |||
* @param documentText The input text | |||
* @param path The path of the value to change. The path represents either to the document root, a property or an array item. | |||
* If the path points to an non-existing property or item, it will be created. | |||
* @param value The new value for the specified property or item. If the value is undefined, | |||
* the property or item will be removed. | |||
* @param options Options | |||
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or | |||
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of | |||
* text in the original document. However, multiple edits can have | |||
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first. | |||
* To apply edits to an input, you can use `applyEdits` | |||
*/ | |||
export declare function modify(text: string, path: JSONPath, value: any, options: ModificationOptions): Edit[]; | |||
/** | |||
* Applies edits to a input string. | |||
*/ | |||
export declare function applyEdits(text: string, edits: Edit[]): string; |
@@ -1,60 +0,0 @@ | |||
{ | |||
"_from": "jsonc-parser@^1.0.0", | |||
"_id": "jsonc-parser@1.0.3", | |||
"_inBundle": false, | |||
"_integrity": "sha512-hk/69oAeaIzchq/v3lS50PXuzn5O2ynldopMC+SWBql7J2WtdptfB9dy8Y7+Og5rPkTCpn83zTiO8FMcqlXJ/g==", | |||
"_location": "/jsonc-parser", | |||
"_phantomChildren": {}, | |||
"_requested": { | |||
"type": "range", | |||
"registry": true, | |||
"raw": "jsonc-parser@^1.0.0", | |||
"name": "jsonc-parser", | |||
"escapedName": "jsonc-parser", | |||
"rawSpec": "^1.0.0", | |||
"saveSpec": null, | |||
"fetchSpec": "^1.0.0" | |||
}, | |||
"_requiredBy": [ | |||
"/vscode-emmet-helper" | |||
], | |||
"_resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-1.0.3.tgz", | |||
"_shasum": "1d53d7160e401a783dbceabaad82473f80e6ad7e", | |||
"_spec": "jsonc-parser@^1.0.0", | |||
"_where": "/tmp/coc-emmet-I4j5XV/node_modules/vscode-emmet-helper", | |||
"author": { | |||
"name": "Microsoft Corporation" | |||
}, | |||
"bugs": { | |||
"url": "https://github.com/Microsoft/node-jsonc-parser/issues" | |||
}, | |||
"bundleDependencies": false, | |||
"deprecated": false, | |||
"description": "Scanner and parser for JSON with comments.", | |||
"devDependencies": { | |||
"@types/mocha": "^2.2.32", | |||
"@types/node": "^7.0.43", | |||
"mocha": "^2.4.5", | |||
"tslint": "^5.9.1", | |||
"typescript": "^2.7.2" | |||
}, | |||
"homepage": "https://github.com/Microsoft/node-jsonc-parser#readme", | |||
"license": "MIT", | |||
"main": "./lib/umd/main.js", | |||
"module": "./lib/esm/main.js", | |||
"name": "jsonc-parser", | |||
"repository": { | |||
"type": "git", | |||
"url": "git+https://github.com/Microsoft/node-jsonc-parser.git" | |||
}, | |||
"scripts": { | |||
"compile": "tsc -p ./src && tsc -p ./src/tsconfig.esm.json", | |||
"postversion": "git push && git push --tags", | |||
"prepare": "npm run compile", | |||
"preversion": "npm test", | |||
"test": "npm run compile && mocha", | |||
"watch": "tsc -w -p ./src" | |||
}, | |||
"typings": "./lib/umd/main", | |||
"version": "1.0.3" | |||
} |
@@ -1,31 +0,0 @@ | |||
THIRD-PARTY SOFTWARE NOTICES AND INFORMATION | |||
For Microsoft vscode-languageserver | |||
This project incorporates material from the project(s) listed below (collectively, “Third Party Code”). | |||
Microsoft is not the original author of the Third Party Code. The original copyright notice and license | |||
under which Microsoft received such Third Party Code are set out below. This Third Party Code is licensed | |||
to you under their original license terms set forth below. Microsoft reserves all other rights not expressly | |||
granted, whether by implication, estoppel or otherwise. | |||
1. DefinitelyTyped version 0.0.1 (https://github.com/borisyankov/DefinitelyTyped) | |||
This project is licensed under the MIT license. | |||
Copyrights are respective of each contributor listed at the beginning of each definition file. | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in | |||
all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
THE SOFTWARE. |
@@ -1,21 +0,0 @@ | |||
The MIT License (MIT) | |||
Copyright (c) Microsoft | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. |
@@ -1,11 +0,0 @@ | |||
# vscode-emmet-helper | |||
A helper module to use emmet modules with Visual Studio Code | |||
Visual Studio Code extensions that provide language service and want to provide emmet abbreviation expansions | |||
in auto-complete can include this module and use the `doComplete` method. | |||
Just pass the one of the emmet supported syntaxes that you would like the completion provider to use along with other parameters that you would generally pass to a completion provider. | |||
If `emmet.includeLanguages` has a mapping for your language, then the builit-in emmet extension will provide | |||
html emmet abbreviations. Ask the user to remove the mapping, if your extension decides to provide | |||
emmet completions using this module |
@@ -1,98 +0,0 @@ | |||
import { TextDocument, Position, Range, CompletionList } from 'vscode-languageserver-types'; | |||
export interface EmmetConfiguration { | |||
showExpandedAbbreviation?: string; | |||
showAbbreviationSuggestions?: boolean; | |||
syntaxProfiles?: object; | |||
variables?: object; | |||
preferences?: object; | |||
excludeLanguages?: string[]; | |||
showSuggestionsAsSnippets?: boolean; | |||
} | |||
export interface ExpandOptions { | |||
field: (index: any, placeholder: any) => string; | |||
syntax: string; | |||
profile: any; | |||
addons: any; | |||
variables: any; | |||
snippets: any; | |||
format: any; | |||
preferences: any; | |||
} | |||
export declare function doComplete(document: TextDocument, position: Position, syntax: string, emmetConfig: EmmetConfiguration): CompletionList; | |||
export declare const emmetSnippetField: (index: any, placeholder: any) => string; | |||
export declare function isStyleSheet(syntax: any): boolean; | |||
/** | |||
* * Extracts abbreviation from the given position in the given document | |||
* @param document The TextDocument from which abbreviation needs to be extracted | |||
* @param position The Position in the given document from where abbreviation needs to be extracted | |||
* @param options The options to pass to the @emmetio/extract-abbreviation module | |||
*/ | |||
export declare function extractAbbreviation(document: TextDocument, position: Position, options?: boolean | { | |||
lookAhead?: boolean; | |||
syntax?: string; | |||
}): { | |||
abbreviation: string; | |||
abbreviationRange: Range; | |||
filter: string; | |||
}; | |||
/** | |||
* Extracts abbreviation from the given text | |||
* @param text Text from which abbreviation needs to be extracted | |||
* @param syntax Syntax used to extract the abbreviation from the given text | |||
*/ | |||
export declare function extractAbbreviationFromText(text: string, syntax?: string): { | |||
abbreviation: string; | |||
filter: string; | |||
}; | |||
/** | |||
* Returns a boolean denoting validity of given abbreviation in the context of given syntax | |||
* Not needed once https://github.com/emmetio/atom-plugin/issues/22 is fixed | |||
* @param syntax string | |||
* @param abbreviation string | |||
*/ | |||
export declare function isAbbreviationValid(syntax: string, abbreviation: string): boolean; | |||
/** | |||
* Returns options to be used by the @emmetio/expand-abbreviation module | |||
* @param syntax | |||
* @param textToReplace | |||
*/ | |||
export declare function getExpandOptions(syntax: string, emmetConfig?: object, filter?: string): ExpandOptions; | |||
/** | |||
* Parses given abbreviation using given options and returns a tree | |||
* @param abbreviation string | |||
* @param options options used by the @emmetio/expand-abbreviation module to parse given abbreviation | |||
*/ | |||
export declare function parseAbbreviation(abbreviation: string, options: ExpandOptions): any; | |||
/** | |||
* Expands given abbreviation using given options | |||
* @param abbreviation string or parsed abbreviation | |||
* @param options options used by the @emmetio/expand-abbreviation module to expand given abbreviation | |||
*/ | |||
export declare function expandAbbreviation(abbreviation: any, options: ExpandOptions): string; | |||
/** | |||
* Updates customizations from snippets.json and syntaxProfiles.json files in the directory configured in emmet.extensionsPath setting | |||
*/ | |||
export declare function updateExtensionsPath(emmetExtensionsPath: string, workspaceFolderPath?: string): Promise<void>; | |||
/** | |||
* Get the corresponding emmet mode for given vscode language mode | |||
* Eg: jsx for typescriptreact/javascriptreact or pug for jade | |||
* If the language is not supported by emmet or has been exlcuded via `exlcudeLanguages` setting, | |||
* then nothing is returned | |||
* | |||
* @param language | |||
* @param exlcudedLanguages Array of language ids that user has chosen to exlcude for emmet | |||
*/ | |||
export declare function getEmmetMode(language: string, excludedLanguages?: string[]): string; | |||
/** | |||
* Returns a completion participant for Emmet of the form { | |||
* onCssProperty: () => void | |||
* onCssPropertyValue: () => void | |||
* onHtmlContent: () => void | |||
* } | |||
* @param document The TextDocument for which completions are being provided | |||
* @param position The Position in the given document where completions are being provided | |||
* @param syntax The Emmet syntax to use when providing Emmet completions | |||
* @param emmetSettings The Emmet settings to use when providing Emmet completions | |||
* @param result The Completion List object that needs to be updated with Emmet completions | |||
*/ | |||
export declare function getEmmetCompletionParticipants(document: TextDocument, position: Position, syntax: string, emmetSettings: EmmetConfiguration, result: CompletionList): any; |
@@ -1,952 +0,0 @@ | |||
"use strict"; | |||
/*--------------------------------------------------------------------------------------------- | |||
* Copyright (c) Microsoft Corporation. All rights reserved. | |||
* Licensed under the MIT License. See License.txt in the project root for license information. | |||
*--------------------------------------------------------------------------------------------*/ | |||
Object.defineProperty(exports, "__esModule", { value: true }); | |||
const vscode_languageserver_types_1 = require("vscode-languageserver-types"); | |||
const expand_full_1 = require("./expand/expand-full"); | |||
const extract = require("@emmetio/extract-abbreviation"); | |||
const path = require("path"); | |||
const fs = require("fs"); | |||
const JSONC = require("jsonc-parser"); | |||
const os_1 = require("os"); | |||
const data_1 = require("./data"); | |||
const snippetKeyCache = new Map(); | |||
let markupSnippetKeys; | |||
let markupSnippetKeysRegex; | |||
const stylesheetCustomSnippetsKeyCache = new Map(); | |||
const htmlAbbreviationStartRegex = /^[a-z,A-Z,!,(,[,#,\.]/; | |||
const cssAbbreviationRegex = /^-?[a-z,A-Z,!,@,#]/; | |||
const htmlAbbreviationRegex = /[a-z,A-Z\.]/; | |||
const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', 'scss', 'sass', 'less', 'stylus']; | |||
const commonlyUsedTags = [...data_1.htmlData.tags, 'lorem']; | |||
const bemFilterSuffix = 'bem'; | |||
const filterDelimitor = '|'; | |||
const trimFilterSuffix = 't'; | |||
const commentFilterSuffix = 'c'; | |||
const maxFilters = 3; | |||
const vendorPrefixes = { 'w': "webkit", 'm': "moz", 's': "ms", 'o': "o" }; | |||
const defaultVendorProperties = { | |||
'w': "animation, animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, appearance, backface-visibility, background-clip, background-composite, background-origin, background-size, border-fit, border-horizontal-spacing, border-image, border-vertical-spacing, box-align, box-direction, box-flex, box-flex-group, box-lines, box-ordinal-group, box-orient, box-pack, box-reflect, box-shadow, color-correction, column-break-after, column-break-before, column-break-inside, column-count, column-gap, column-rule-color, column-rule-style, column-rule-width, column-span, column-width, dashboard-region, font-smoothing, highlight, hyphenate-character, hyphenate-limit-after, hyphenate-limit-before, hyphens, line-box-contain, line-break, line-clamp, locale, margin-before-collapse, margin-after-collapse, marquee-direction, marquee-increment, marquee-repetition, marquee-style, mask-attachment, mask-box-image, mask-box-image-outset, mask-box-image-repeat, mask-box-image-slice, mask-box-image-source, mask-box-image-width, mask-clip, mask-composite, mask-image, mask-origin, mask-position, mask-repeat, mask-size, nbsp-mode, perspective, perspective-origin, rtl-ordering, text-combine, text-decorations-in-effect, text-emphasis-color, text-emphasis-position, text-emphasis-style, text-fill-color, text-orientation, text-security, text-stroke-color, text-stroke-width, transform, transition, transform-origin, transform-style, transition-delay, transition-duration, transition-property, transition-timing-function, user-drag, user-modify, user-select, writing-mode, svg-shadow, box-sizing, border-radius", | |||
'm': "animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, appearance, backface-visibility, background-inline-policy, binding, border-bottom-colors, border-image, border-left-colors, border-right-colors, border-top-colors, box-align, box-direction, box-flex, box-ordinal-group, box-orient, box-pack, box-shadow, box-sizing, column-count, column-gap, column-rule-color, column-rule-style, column-rule-width, column-width, float-edge, font-feature-settings, font-language-override, force-broken-image-icon, hyphens, image-region, orient, outline-radius-bottomleft, outline-radius-bottomright, outline-radius-topleft, outline-radius-topright, perspective, perspective-origin, stack-sizing, tab-size, text-blink, text-decoration-color, text-decoration-line, text-decoration-style, text-size-adjust, transform, transform-origin, transform-style, transition, transition-delay, transition-duration, transition-property, transition-timing-function, user-focus, user-input, user-modify, user-select, window-shadow, background-clip, border-radius", | |||
's': "accelerator, backface-visibility, background-position-x, background-position-y, behavior, block-progression, box-align, box-direction, box-flex, box-line-progression, box-lines, box-ordinal-group, box-orient, box-pack, content-zoom-boundary, content-zoom-boundary-max, content-zoom-boundary-min, content-zoom-chaining, content-zoom-snap, content-zoom-snap-points, content-zoom-snap-type, content-zooming, filter, flow-from, flow-into, font-feature-settings, grid-column, grid-column-align, grid-column-span, grid-columns, grid-layer, grid-row, grid-row-align, grid-row-span, grid-rows, high-contrast-adjust, hyphenate-limit-chars, hyphenate-limit-lines, hyphenate-limit-zone, hyphens, ime-mode, interpolation-mode, layout-flow, layout-grid, layout-grid-char, layout-grid-line, layout-grid-mode, layout-grid-type, line-break, overflow-style, perspective, perspective-origin, perspective-origin-x, perspective-origin-y, scroll-boundary, scroll-boundary-bottom, scroll-boundary-left, scroll-boundary-right, scroll-boundary-top, scroll-chaining, scroll-rails, scroll-snap-points-x, scroll-snap-points-y, scroll-snap-type, scroll-snap-x, scroll-snap-y, scrollbar-arrow-color, scrollbar-base-color, scrollbar-darkshadow-color, scrollbar-face-color, scrollbar-highlight-color, scrollbar-shadow-color, scrollbar-track-color, text-align-last, text-autospace, text-justify, text-kashida-space, text-overflow, text-size-adjust, text-underline-position, touch-action, transform, transform-origin, transform-origin-x, transform-origin-y, transform-origin-z, transform-style, transition, transition-delay, transition-duration, transition-property, transition-timing-function, user-select, word-break, wrap-flow, wrap-margin, wrap-through, writing-mode", | |||
'o': "dashboard-region, animation, animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, border-image, link, link-source, object-fit, object-position, tab-size, table-baseline, transform, transform-origin, transition, transition-delay, transition-duration, transition-property, transition-timing-function, accesskey, input-format, input-required, marquee-dir, marquee-loop, marquee-speed, marquee-style" | |||
}; | |||
/** | |||
* Returns all applicable emmet expansions for abbreviation at given position in a CompletionList | |||
* @param document TextDocument in which completions are requested | |||
* @param position Position in the document at which completions are requested | |||
* @param syntax Emmet supported language | |||
* @param emmetConfig Emmet Configurations as derived from VS Code | |||
*/ | |||
function doComplete(document, position, syntax, emmetConfig) { | |||
if (emmetConfig.showExpandedAbbreviation === 'never' || !getEmmetMode(syntax, emmetConfig.excludeLanguages)) { | |||
return; | |||
} | |||
// Fetch markupSnippets so that we can provide possible abbreviation completions | |||
// For example, when text at position is `a`, completions should return `a:blank`, `a:link`, `acr` etc. | |||
if (!isStyleSheet(syntax)) { | |||
if (!snippetKeyCache.has(syntax) || !markupSnippetKeysRegex || markupSnippetKeysRegex.length === 0) { | |||
let registry = customSnippetRegistry[syntax] ? customSnippetRegistry[syntax] : expand_full_1.createSnippetsRegistry(syntax); | |||
if (!snippetKeyCache.has(syntax)) { | |||
snippetKeyCache.set(syntax, registry.all({ type: 'string' }).map(snippet => { | |||
return snippet.key; | |||
})); | |||
} | |||
markupSnippetKeysRegex = registry.all({ type: 'regexp' }).map(snippet => { | |||
return snippet.key; | |||
}); | |||
} | |||
markupSnippetKeys = snippetKeyCache.get(syntax); | |||
} | |||
let extractedValue = extractAbbreviation(document, position, { syntax, lookAhead: !isStyleSheet(syntax) }); | |||
if (!extractedValue) { | |||
return; | |||
} | |||
let { abbreviationRange, abbreviation, filter } = extractedValue; | |||
let currentLineTillPosition = getCurrentLine(document, position).substr(0, position.character); | |||
let currentWord = getCurrentWord(currentLineTillPosition); | |||
// Dont attempt to expand open tags | |||
if (currentWord === abbreviation | |||
&& currentLineTillPosition.endsWith(`<${abbreviation}`) | |||
&& (syntax === 'html' || syntax === 'xml' || syntax === 'xsl' || syntax === 'jsx')) { | |||
return; | |||
} | |||
// `preferences` is supported in Emmet config to allow backward compatibility | |||
// `getExpandOptions` converts it into a format understandable by new modules | |||
// We retain a copy here to be used by the vendor prefixing feature | |||
let expandOptions = getExpandOptions(syntax, emmetConfig, filter); | |||
let preferences = expandOptions['preferences']; | |||
delete expandOptions['preferences']; | |||
let expandedText; | |||
let expandedAbbr; | |||
let completionItems = []; | |||
// Create completion item after expanding given abbreviation | |||
// if abbreviation is valid and expanded value is not noise | |||
const createExpandedAbbr = (syntax, abbr) => { | |||
if (!isAbbreviationValid(syntax, abbreviation)) { | |||
return; | |||
} | |||
try { | |||
expandedText = expand_full_1.expand(abbr, expandOptions); | |||
} | |||
catch (e) { | |||
} | |||
if (!expandedText || isExpandedTextNoise(syntax, abbr, expandedText)) { | |||
return; | |||
} | |||
expandedAbbr = vscode_languageserver_types_1.CompletionItem.create(abbr); | |||
expandedAbbr.textEdit = vscode_languageserver_types_1.TextEdit.replace(abbreviationRange, escapeNonTabStopDollar(addFinalTabStop(expandedText))); | |||
expandedAbbr.documentation = replaceTabStopsWithCursors(expandedText); | |||
expandedAbbr.insertTextFormat = vscode_languageserver_types_1.InsertTextFormat.Snippet; | |||
expandedAbbr.detail = 'Emmet Abbreviation'; | |||
expandedAbbr.label = abbreviation; | |||
expandedAbbr.label += filter ? '|' + filter.replace(',', '|') : ""; | |||
completionItems = [expandedAbbr]; | |||
}; | |||
if (isStyleSheet(syntax)) { | |||
let { prefixOptions, abbreviationWithoutPrefix } = splitVendorPrefix(abbreviation); | |||
createExpandedAbbr(syntax, abbreviationWithoutPrefix); | |||
// When abbr is longer than usual emmet snippets and matches better with existing css property, then no emmet | |||
if (abbreviationWithoutPrefix.length > 4 | |||
&& data_1.cssData.properties.find(x => x.startsWith(abbreviationWithoutPrefix))) { | |||
return vscode_languageserver_types_1.CompletionList.create([], true); | |||
} | |||
if (expandedAbbr) { | |||
let prefixedExpandedText = applyVendorPrefixes(expandedText, prefixOptions, preferences); | |||
expandedAbbr.textEdit = vscode_languageserver_types_1.TextEdit.replace(abbreviationRange, escapeNonTabStopDollar(addFinalTabStop(prefixedExpandedText))); | |||
expandedAbbr.documentation = replaceTabStopsWithCursors(prefixedExpandedText); | |||
expandedAbbr.label = removeTabStops(expandedText); | |||
expandedAbbr.filterText = abbreviation; | |||
// Custom snippets should show up in completions if abbreviation is a prefix | |||
const stylesheetCustomSnippetsKeys = stylesheetCustomSnippetsKeyCache.has(syntax) ? stylesheetCustomSnippetsKeyCache.get(syntax) : stylesheetCustomSnippetsKeyCache.get('css'); | |||
completionItems = makeSnippetSuggestion(stylesheetCustomSnippetsKeys, abbreviation, abbreviation, abbreviationRange, expandOptions, 'Emmet Custom Snippet', false); | |||
if (!completionItems.find(x => x.textEdit.newText === expandedAbbr.textEdit.newText)) { | |||
// Fix for https://github.com/Microsoft/vscode/issues/28933#issuecomment-309236902 | |||
// When user types in propertyname, emmet uses it to match with snippet names, resulting in width -> widows or font-family -> font: family | |||
// Filter out those cases here. | |||
const abbrRegex = new RegExp('.*' + abbreviationWithoutPrefix.split('').map(x => (x === '$' || x === '+') ? '\\' + x : x).join('.*') + '.*', 'i'); | |||
if (/\d/.test(abbreviation) || abbrRegex.test(expandedAbbr.label)) { | |||
completionItems.push(expandedAbbr); | |||
} | |||
} | |||
} | |||
// Incomplete abbreviations that use vendor prefix | |||
if (!completionItems.length && (abbreviation === '-' || /^-[wmso]{1,4}-?$/.test(abbreviation))) { | |||
return vscode_languageserver_types_1.CompletionList.create([], true); | |||
} | |||
} | |||
else { | |||
createExpandedAbbr(syntax, abbreviation); | |||
let tagToFindMoreSuggestionsFor = abbreviation; | |||
let newTagMatches = abbreviation.match(/(>|\+)([\w:-]+)$/); | |||
if (newTagMatches && newTagMatches.length === 3) { | |||
tagToFindMoreSuggestionsFor = newTagMatches[2]; | |||
} | |||
let commonlyUsedTagSuggestions = makeSnippetSuggestion(commonlyUsedTags, tagToFindMoreSuggestionsFor, abbreviation, abbreviationRange, expandOptions, 'Emmet Abbreviation'); | |||
completionItems = completionItems.concat(commonlyUsedTagSuggestions); | |||
if (emmetConfig.showAbbreviationSuggestions === true) { | |||
let abbreviationSuggestions = makeSnippetSuggestion(markupSnippetKeys.filter(x => !commonlyUsedTags.includes(x)), tagToFindMoreSuggestionsFor, abbreviation, abbreviationRange, expandOptions, 'Emmet Abbreviation'); | |||
// Workaround for the main expanded abbr not appearing before the snippet suggestions | |||
if (expandedAbbr && abbreviationSuggestions.length > 0 && tagToFindMoreSuggestionsFor !== abbreviation) { | |||
expandedAbbr.sortText = '0' + expandedAbbr.label; | |||
abbreviationSuggestions.forEach(item => { | |||
// Workaround for snippet suggestions items getting filtered out as the complete abbr does not start with snippetKey | |||
item.filterText = abbreviation; | |||
// Workaround for the main expanded abbr not appearing before the snippet suggestions | |||
item.sortText = '9' + abbreviation; | |||
}); | |||
} | |||
completionItems = completionItems.concat(abbreviationSuggestions); | |||
} | |||
} | |||
if (emmetConfig.showSuggestionsAsSnippets === true) { | |||
completionItems.forEach(x => x.kind = vscode_languageserver_types_1.CompletionItemKind.Snippet); | |||
} | |||
return completionItems.length ? vscode_languageserver_types_1.CompletionList.create(completionItems, true) : undefined; | |||
} | |||
exports.doComplete = doComplete; | |||
/** | |||
* Create & return snippets for snippet keys that start with given prefix | |||
*/ | |||
function makeSnippetSuggestion(snippetKeys, prefix, abbreviation, abbreviationRange, expandOptions, snippetDetail, skipFullMatch = true) { | |||
if (!prefix || !snippetKeys) { | |||
return []; | |||
} | |||
let snippetCompletions = []; | |||
snippetKeys.forEach(snippetKey => { | |||
if (!snippetKey.startsWith(prefix.toLowerCase()) || (skipFullMatch && snippetKey === prefix.toLowerCase())) { | |||
return; | |||
} | |||
let currentAbbr = abbreviation + snippetKey.substr(prefix.length); | |||
let expandedAbbr; | |||
try { | |||
expandedAbbr = expand_full_1.expand(currentAbbr, expandOptions); | |||
} | |||
catch (e) { | |||
} | |||
if (!expandedAbbr) { | |||
return; | |||
} | |||
let item = vscode_languageserver_types_1.CompletionItem.create(prefix + snippetKey.substr(prefix.length)); | |||
item.documentation = replaceTabStopsWithCursors(expandedAbbr); | |||
item.detail = snippetDetail; | |||
item.textEdit = vscode_languageserver_types_1.TextEdit.replace(abbreviationRange, escapeNonTabStopDollar(addFinalTabStop(expandedAbbr))); | |||
item.insertTextFormat = vscode_languageserver_types_1.InsertTextFormat.Snippet; | |||
snippetCompletions.push(item); | |||
}); | |||
return snippetCompletions; | |||
} | |||
function getCurrentWord(currentLineTillPosition) { | |||
if (currentLineTillPosition) { | |||
let matches = currentLineTillPosition.match(/[\w,:,-,\.]*$/); | |||
if (matches) { | |||
return matches[0]; | |||
} | |||
} | |||
} | |||
function replaceTabStopsWithCursors(expandedWord) { | |||
return expandedWord.replace(/([^\\])\$\{\d+\}/g, '$1|').replace(/\$\{\d+:([^\}]+)\}/g, '$1'); | |||
} | |||
function removeTabStops(expandedWord) { | |||
return expandedWord.replace(/([^\\])\$\{\d+\}/g, '$1').replace(/\$\{\d+:([^\}]+)\}/g, '$1'); | |||
} | |||
function escapeNonTabStopDollar(text) { | |||
return text ? text.replace(/([^\\])(\$)([^\{])/g, '$1\\$2$3') : text; | |||
} | |||
function addFinalTabStop(text) { | |||
if (!text || !text.trim()) { | |||
return text; | |||
} | |||
let maxTabStop = -1; | |||
let maxTabStopRanges = []; | |||
let foundLastStop = false; | |||
let replaceWithLastStop = false; | |||
let i = 0; | |||
let n = text.length; | |||
try { | |||
while (i < n && !foundLastStop) { | |||
// Look for ${ | |||
if (text[i++] != '$' || text[i++] != '{') { | |||
continue; | |||
} | |||
// Find tabstop | |||
let numberStart = -1; | |||
let numberEnd = -1; | |||
while (i < n && /\d/.test(text[i])) { | |||
numberStart = numberStart < 0 ? i : numberStart; | |||
numberEnd = i + 1; | |||
i++; | |||
} | |||
// If ${ was not followed by a number and either } or :, then its not a tabstop | |||
if (numberStart === -1 || numberEnd === -1 || i >= n || (text[i] != '}' && text[i] != ':')) { | |||
continue; | |||
} | |||
// If ${0} was found, then break | |||
const currentTabStop = text.substring(numberStart, numberEnd); | |||
foundLastStop = currentTabStop === '0'; | |||
if (foundLastStop) { | |||
break; | |||
} | |||
let foundPlaceholder = false; | |||
if (text[i++] == ':') { | |||
// TODO: Nested placeholders may break here | |||
while (i < n) { | |||
if (text[i] == '}') { | |||
foundPlaceholder = true; | |||
break; | |||
} | |||
i++; | |||
} | |||
} | |||
// Decide to replace currentTabStop with ${0} only if its the max among all tabstops and is not a placeholder | |||
if (Number(currentTabStop) > Number(maxTabStop)) { | |||
maxTabStop = currentTabStop; | |||
maxTabStopRanges = [{ numberStart, numberEnd }]; | |||
replaceWithLastStop = !foundPlaceholder; | |||
} | |||
else if (currentTabStop == maxTabStop) { | |||
maxTabStopRanges.push({ numberStart, numberEnd }); | |||
} | |||
} | |||
} | |||
catch (e) { | |||
} | |||
if (replaceWithLastStop && !foundLastStop) { | |||
for (let i = 0; i < maxTabStopRanges.length; i++) { | |||
let rangeStart = maxTabStopRanges[i].numberStart; | |||
let rangeEnd = maxTabStopRanges[i].numberEnd; | |||
text = text.substr(0, rangeStart) + '0' + text.substr(rangeEnd); | |||
} | |||
} | |||
return text; | |||
} | |||
function getCurrentLine(document, position) { | |||
let offset = document.offsetAt(position); | |||
let text = document.getText(); | |||
let start = 0; | |||
let end = text.length; | |||
for (let i = offset - 1; i >= 0; i--) { | |||
if (text[i] === '\n') { | |||
start = i + 1; | |||
break; | |||
} | |||
} | |||
for (let i = offset; i < text.length; i++) { | |||
if (text[i] === '\n') { | |||
end = i; | |||
break; | |||
} | |||
} | |||
return text.substring(start, end); | |||
} | |||
let customSnippetRegistry = {}; | |||
let variablesFromFile = {}; | |||
let profilesFromFile = {}; | |||
exports.emmetSnippetField = (index, placeholder) => `\${${index}${placeholder ? ':' + placeholder : ''}}`; | |||
function isStyleSheet(syntax) { | |||
let stylesheetSyntaxes = ['css', 'scss', 'sass', 'less', 'stylus']; | |||
return (stylesheetSyntaxes.indexOf(syntax) > -1); | |||
} | |||
exports.isStyleSheet = isStyleSheet; | |||
function getFilters(text, pos) { | |||
let filter; | |||
for (let i = 0; i < maxFilters; i++) { | |||
if (text.endsWith(`${filterDelimitor}${bemFilterSuffix}`, pos)) { | |||
pos -= bemFilterSuffix.length + 1; | |||
filter = filter ? bemFilterSuffix + ',' + filter : bemFilterSuffix; | |||
} | |||
else if (text.endsWith(`${filterDelimitor}${commentFilterSuffix}`, pos)) { | |||
pos -= commentFilterSuffix.length + 1; | |||
filter = filter ? commentFilterSuffix + ',' + filter : commentFilterSuffix; | |||
} | |||
else if (text.endsWith(`${filterDelimitor}${trimFilterSuffix}`, pos)) { | |||
pos -= trimFilterSuffix.length + 1; | |||
filter = filter ? trimFilterSuffix + ',' + filter : trimFilterSuffix; | |||
} | |||
else { | |||
break; | |||
} | |||
} | |||
return { | |||
pos: pos, | |||
filter: filter | |||
}; | |||
} | |||
/** | |||
* * Extracts abbreviation from the given position in the given document | |||
* @param document The TextDocument from which abbreviation needs to be extracted | |||
* @param position The Position in the given document from where abbreviation needs to be extracted | |||
* @param options The options to pass to the @emmetio/extract-abbreviation module | |||
*/ | |||
function extractAbbreviation(document, position, options) { | |||
const currentLine = getCurrentLine(document, position); | |||
const currentLineTillPosition = currentLine.substr(0, position.character); | |||
const { pos, filter } = getFilters(currentLineTillPosition, position.character); | |||
const lengthOccupiedByFilter = filter ? filter.length + 1 : 0; | |||
try { | |||
let extractOptions = options; | |||
if (typeof extractOptions !== 'boolean') { | |||
extractOptions = extractOptions || {}; | |||
extractOptions = { | |||
syntax: (isStyleSheet(extractOptions.syntax) || extractOptions.syntax === 'stylesheet') ? 'stylesheet' : 'markup', | |||
lookAhead: extractOptions.lookAhead | |||
}; | |||
} | |||
const result = extract(currentLine, pos, extractOptions); | |||
const rangeToReplace = vscode_languageserver_types_1.Range.create(position.line, result.location, position.line, result.location + result.abbreviation.length + lengthOccupiedByFilter); | |||
return { | |||
abbreviationRange: rangeToReplace, | |||
abbreviation: result.abbreviation, | |||
filter | |||
}; | |||
} | |||
catch (e) { | |||
} | |||
} | |||
exports.extractAbbreviation = extractAbbreviation; | |||
/** | |||
* Extracts abbreviation from the given text | |||
* @param text Text from which abbreviation needs to be extracted | |||
* @param syntax Syntax used to extract the abbreviation from the given text | |||
*/ | |||
function extractAbbreviationFromText(text, syntax) { | |||
if (!text) { | |||
return; | |||
} | |||
const { pos, filter } = getFilters(text, text.length); | |||
try { | |||
let extractOptions = (isStyleSheet(syntax) || syntax === 'stylesheet') ? { syntax: 'stylesheet', lookAhead: false } : true; | |||
const result = extract(text, pos, extractOptions); | |||
return { | |||
abbreviation: result.abbreviation, | |||
filter | |||
}; | |||
} | |||
catch (e) { | |||
} | |||
} | |||
exports.extractAbbreviationFromText = extractAbbreviationFromText; | |||
/** | |||
* Returns a boolean denoting validity of given abbreviation in the context of given syntax | |||
* Not needed once https://github.com/emmetio/atom-plugin/issues/22 is fixed | |||
* @param syntax string | |||
* @param abbreviation string | |||
*/ | |||
function isAbbreviationValid(syntax, abbreviation) { | |||
if (!abbreviation) { | |||
return false; | |||
} | |||
if (isStyleSheet(syntax)) { | |||
// Fix for https://github.com/Microsoft/vscode/issues/1623 in new emmet | |||
if (abbreviation.endsWith(':')) { | |||
return false; | |||
} | |||
if (abbreviation.indexOf('#') > -1) { | |||
return hexColorRegex.test(abbreviation) || propertyHexColorRegex.test(abbreviation); | |||
} | |||
return cssAbbreviationRegex.test(abbreviation); | |||
} | |||
if (abbreviation.startsWith('!')) { | |||
return !/[^!]/.test(abbreviation); | |||
} | |||
const multipleMatch = abbreviation.match(/\*(\d+)$/); | |||
if (multipleMatch) { | |||
return parseInt(multipleMatch[1], 10) <= 100; | |||
} | |||
// Its common for users to type (sometextinsidebrackets), this should not be treated as an abbreviation | |||
// Grouping in abbreviation is valid only if it's inside a text node or preceeded/succeeded with one of the symbols for nesting, sibling, repeater or climb up | |||
if ((/\(/.test(abbreviation) || /\)/.test(abbreviation)) && !/\{[^\}\{]*[\(\)]+[^\}\{]*\}(?:[>\+\*\^]|$)/.test(abbreviation) && !/\(.*\)[>\+\*\^]/.test(abbreviation) && !/[>\+\*\^]\(.*\)/.test(abbreviation)) { | |||
return false; | |||
} | |||
return (htmlAbbreviationStartRegex.test(abbreviation) && htmlAbbreviationRegex.test(abbreviation)); | |||
} | |||
exports.isAbbreviationValid = isAbbreviationValid; | |||
function isExpandedTextNoise(syntax, abbreviation, expandedText) { | |||
// Unresolved css abbreviations get expanded to a blank property value | |||
// Eg: abc -> abc: ; or abc:d -> abc: d; which is noise if it gets suggested for every word typed | |||
if (isStyleSheet(syntax)) { | |||
let after = (syntax === 'sass' || syntax === 'stylus') ? '' : ';'; | |||
return expandedText === `${abbreviation}: \${1}${after}` || expandedText.replace(/\s/g, '') === abbreviation.replace(/\s/g, '') + after; | |||
} | |||
if (commonlyUsedTags.indexOf(abbreviation.toLowerCase()) > -1 || markupSnippetKeys.indexOf(abbreviation) > -1) { | |||
return false; | |||
} | |||
// Custom tags can have - or : | |||
if (/[-,:]/.test(abbreviation) && !/--|::/.test(abbreviation) && !abbreviation.endsWith(':')) { | |||
return false; | |||
} | |||
// Its common for users to type some text and end it with period, this should not be treated as an abbreviation | |||
// Else it becomes noise. | |||
// When user just types '.', return the expansion | |||
// Otherwise emmet loses change to participate later | |||
// For example in `.foo`. See https://github.com/Microsoft/vscode/issues/66013 | |||
if (abbreviation === '.') { | |||
return false; | |||
} | |||
const dotMatches = abbreviation.match(/^([a-z,A-Z,\d]*)\.$/); | |||
if (dotMatches) { | |||
// Valid html tags such as `div.` | |||
if (dotMatches[1] && data_1.htmlData.tags.includes(dotMatches[1])) { | |||
return false; | |||
} | |||
return true; | |||
} | |||
// Unresolved html abbreviations get expanded as if it were a tag | |||
// Eg: abc -> <abc></abc> which is noise if it gets suggested for every word typed | |||
return (expandedText.toLowerCase() === `<${abbreviation.toLowerCase()}>\${1}</${abbreviation.toLowerCase()}>`); | |||
} | |||
/** | |||
* Returns options to be used by the @emmetio/expand-abbreviation module | |||
* @param syntax | |||
* @param textToReplace | |||
*/ | |||
function getExpandOptions(syntax, emmetConfig, filter) { | |||
emmetConfig = emmetConfig || {}; | |||
emmetConfig['preferences'] = emmetConfig['preferences'] || {}; | |||
// Fetch snippet registry | |||
let baseSyntax = isStyleSheet(syntax) ? 'css' : 'html'; | |||
if (!customSnippetRegistry[syntax] && customSnippetRegistry[baseSyntax]) { | |||
customSnippetRegistry[syntax] = customSnippetRegistry[baseSyntax]; | |||
} | |||
// Fetch Profile | |||
let profile = getProfile(syntax, emmetConfig['syntaxProfiles']); | |||
let filtersFromProfile = (profile && profile['filters']) ? profile['filters'].split(',') : []; | |||
filtersFromProfile = filtersFromProfile.map(filterFromProfile => filterFromProfile.trim()); | |||
// Update profile based on preferences | |||
if (emmetConfig['preferences']['format.noIndentTags']) { | |||
if (Array.isArray(emmetConfig['preferences']['format.noIndentTags'])) { | |||
profile['formatSkip'] = emmetConfig['preferences']['format.noIndentTags']; | |||
} | |||
else if (typeof emmetConfig['preferences']['format.noIndentTags'] === 'string') { | |||
profile['formatSkip'] = emmetConfig['preferences']['format.noIndentTags'].split(','); | |||
} | |||
} | |||
if (emmetConfig['preferences']['format.forceIndentationForTags']) { | |||
if (Array.isArray(emmetConfig['preferences']['format.forceIndentationForTags'])) { | |||
profile['formatForce'] = emmetConfig['preferences']['format.forceIndentationForTags']; | |||
} | |||
else if (typeof emmetConfig['preferences']['format.forceIndentationForTags'] === 'string') { | |||
profile['formatForce'] = emmetConfig['preferences']['format.forceIndentationForTags'].split(','); | |||
} | |||
} | |||
if (emmetConfig['preferences']['profile.allowCompactBoolean'] && typeof emmetConfig['preferences']['profile.allowCompactBoolean'] === 'boolean') { | |||
profile['compactBooleanAttributes'] = emmetConfig['preferences']['profile.allowCompactBoolean']; | |||
} | |||
// Fetch Add Ons | |||
let addons = {}; | |||
if (filter && filter.split(',').find(x => x.trim() === 'bem') || filtersFromProfile.indexOf('bem') > -1) { | |||
addons['bem'] = { element: '__' }; | |||
if (emmetConfig['preferences']['bem.elementSeparator']) { | |||
addons['bem']['element'] = emmetConfig['preferences']['bem.elementSeparator']; | |||
} | |||
if (emmetConfig['preferences']['bem.modifierSeparator']) { | |||
addons['bem']['modifier'] = emmetConfig['preferences']['bem.modifierSeparator']; | |||
} | |||
} | |||
if (syntax === 'jsx') { | |||
addons['jsx'] = true; | |||
} | |||
// Fetch Formatters | |||
let formatters = getFormatters(syntax, emmetConfig['preferences']); | |||
if (filter && filter.split(',').find(x => x.trim() === 'c') || filtersFromProfile.indexOf('c') > -1) { | |||
if (!formatters['comment']) { | |||
formatters['comment'] = { | |||
enabled: true | |||
}; | |||
} | |||
else { | |||
formatters['comment']['enabled'] = true; | |||
} | |||
} | |||
// If the user doesn't provide specific properties for a vendor, use the default values | |||
let preferences = emmetConfig['preferences']; | |||
for (const v in vendorPrefixes) { | |||
let vendorProperties = preferences['css.' + vendorPrefixes[v] + 'Properties']; | |||
if (vendorProperties == null) { | |||
preferences['css.' + vendorPrefixes[v] + 'Properties'] = defaultVendorProperties[v]; | |||
} | |||
} | |||
return { | |||
field: exports.emmetSnippetField, | |||
syntax: syntax, | |||
profile: profile, | |||
addons: addons, | |||
variables: getVariables(emmetConfig['variables']), | |||
snippets: customSnippetRegistry[syntax], | |||
format: formatters, | |||
preferences: preferences | |||
}; | |||
} | |||
exports.getExpandOptions = getExpandOptions; | |||
function splitVendorPrefix(abbreviation) { | |||
abbreviation = abbreviation || ""; | |||
if (abbreviation[0] != '-') { | |||
return { | |||
prefixOptions: "", | |||
abbreviationWithoutPrefix: abbreviation | |||
}; | |||
} | |||
else { | |||
abbreviation = abbreviation.substr(1); | |||
let pref = "-"; | |||
if (/^[wmso]*-./.test(abbreviation)) { | |||
let index = abbreviation.indexOf("-"); | |||
if (index > -1) { | |||
pref += abbreviation.substr(0, index + 1); | |||
abbreviation = abbreviation.substr(index + 1); | |||
} | |||
} | |||
return { | |||
prefixOptions: pref, | |||
abbreviationWithoutPrefix: abbreviation | |||
}; | |||
} | |||
} | |||
function applyVendorPrefixes(expandedProperty, vendors, preferences) { | |||
preferences = preferences || {}; | |||
expandedProperty = expandedProperty || ""; | |||
vendors = vendors || ""; | |||
if (vendors[0] !== '-') { | |||
return expandedProperty; | |||
} | |||
if (vendors == "-") { | |||
let defaultVendors = "-"; | |||
let property = expandedProperty.substr(0, expandedProperty.indexOf(':')); | |||
if (!property) { | |||
return expandedProperty; | |||
} | |||
for (const v in vendorPrefixes) { | |||
let vendorProperties = preferences['css.' + vendorPrefixes[v] + 'Properties']; | |||
if (vendorProperties && vendorProperties.split(',').find(x => x.trim() === property)) | |||
defaultVendors += v; | |||
} | |||
// If no vendors specified, add all | |||
vendors = defaultVendors == "-" ? "-wmso" : defaultVendors; | |||
vendors += '-'; | |||
} | |||
vendors = vendors.substr(1); | |||
let prefixedProperty = ""; | |||
for (let index = 0; index < vendors.length - 1; index++) { | |||
prefixedProperty += '-' + vendorPrefixes[vendors[index]] + '-' + expandedProperty + "\n"; | |||
} | |||
return prefixedProperty + expandedProperty; | |||
} | |||
/** | |||
* Parses given abbreviation using given options and returns a tree | |||
* @param abbreviation string | |||
* @param options options used by the @emmetio/expand-abbreviation module to parse given abbreviation | |||
*/ | |||
function parseAbbreviation(abbreviation, options) { | |||
return expand_full_1.parse(abbreviation, options); | |||
} | |||
exports.parseAbbreviation = parseAbbreviation; | |||
/** | |||
* Expands given abbreviation using given options | |||
* @param abbreviation string or parsed abbreviation | |||
* @param options options used by the @emmetio/expand-abbreviation module to expand given abbreviation | |||
*/ | |||
function expandAbbreviation(abbreviation, options) { | |||
let expandedText; | |||
let preferences = options['preferences']; | |||
delete options['preferences']; | |||
if (isStyleSheet(options['syntax']) && typeof abbreviation === 'string') { | |||
let { prefixOptions, abbreviationWithoutPrefix } = splitVendorPrefix(abbreviation); | |||
expandedText = expand_full_1.expand(abbreviationWithoutPrefix, options); | |||
expandedText = applyVendorPrefixes(expandedText, prefixOptions, preferences); | |||
} | |||
else { | |||
expandedText = expand_full_1.expand(abbreviation, options); | |||
} | |||
return escapeNonTabStopDollar(addFinalTabStop(expandedText)); | |||
} | |||
exports.expandAbbreviation = expandAbbreviation; | |||
/** | |||
* Maps and returns syntaxProfiles of previous format to ones compatible with new emmet modules | |||
* @param syntax | |||
*/ | |||
function getProfile(syntax, profilesFromSettings) { | |||
if (!profilesFromSettings) { | |||
profilesFromSettings = {}; | |||
} | |||
let profilesConfig = Object.assign({}, profilesFromFile, profilesFromSettings); | |||
let options = profilesConfig[syntax]; | |||
if (!options || typeof options === 'string') { | |||
if (options === 'xhtml') { | |||
return { | |||
selfClosingStyle: 'xhtml' | |||
}; | |||
} | |||
return {}; | |||
} | |||
let newOptions = {}; | |||
for (let key in options) { | |||
switch (key) { | |||
case 'tag_case': | |||
newOptions['tagCase'] = (options[key] === 'lower' || options[key] === 'upper') ? options[key] : ''; | |||
break; | |||
case 'attr_case': | |||
newOptions['attributeCase'] = (options[key] === 'lower' || options[key] === 'upper') ? options[key] : ''; | |||
break; | |||
case 'attr_quotes': | |||
newOptions['attributeQuotes'] = options[key]; | |||
break; | |||
case 'tag_nl': | |||
newOptions['format'] = (options[key] === true || options[key] === false) ? options[key] : true; | |||
break; | |||
case 'inline_break': | |||
newOptions['inlineBreak'] = options[key]; | |||
break; | |||
case 'self_closing_tag': | |||
if (options[key] === true) { | |||
newOptions['selfClosingStyle'] = 'xml'; | |||
break; | |||
} | |||
if (options[key] === false) { | |||
newOptions['selfClosingStyle'] = 'html'; | |||
break; | |||
} | |||
newOptions['selfClosingStyle'] = options[key]; | |||
break; | |||
case 'compact_bool': | |||
newOptions['compactBooleanAttributes'] = options[key]; | |||
break; | |||
default: | |||
newOptions[key] = options[key]; | |||
break; | |||
} | |||
} | |||
return newOptions; | |||
} | |||
/** | |||
* Returns variables to be used while expanding snippets | |||
*/ | |||
function getVariables(variablesFromSettings) { | |||
if (!variablesFromSettings) { | |||
return variablesFromFile; | |||
} | |||
return Object.assign({}, variablesFromFile, variablesFromSettings); | |||
} | |||
function getFormatters(syntax, preferences) { | |||
if (!preferences) { | |||
return {}; | |||
} | |||
if (!isStyleSheet(syntax)) { | |||
let commentFormatter = {}; | |||
for (let key in preferences) { | |||
switch (key) { | |||
case 'filter.commentAfter': | |||
commentFormatter['after'] = preferences[key]; | |||
break; | |||
case 'filter.commentBefore': | |||
commentFormatter['before'] = preferences[key]; | |||
break; | |||
case 'filter.commentTrigger': | |||
commentFormatter['trigger'] = preferences[key]; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
return { | |||
comment: commentFormatter | |||
}; | |||
} | |||
let fuzzySearchMinScore = typeof preferences['css.fuzzySearchMinScore'] === 'number' ? preferences['css.fuzzySearchMinScore'] : 0.3; | |||
if (fuzzySearchMinScore > 1) { | |||
fuzzySearchMinScore = 1; | |||
} | |||
else if (fuzzySearchMinScore < 0) { | |||
fuzzySearchMinScore = 0; | |||
} | |||
let stylesheetFormatter = { | |||
'fuzzySearchMinScore': fuzzySearchMinScore | |||
}; | |||
for (let key in preferences) { | |||
switch (key) { | |||
case 'css.floatUnit': | |||
stylesheetFormatter['floatUnit'] = preferences[key]; | |||
break; | |||
case 'css.intUnit': | |||
stylesheetFormatter['intUnit'] = preferences[key]; | |||
break; | |||
case 'css.unitAliases': | |||
let unitAliases = {}; | |||
preferences[key].split(',').forEach(alias => { | |||
if (!alias || !alias.trim() || alias.indexOf(':') === -1) { | |||
return; | |||
} | |||
let aliasName = alias.substr(0, alias.indexOf(':')); | |||
let aliasValue = alias.substr(aliasName.length + 1); | |||
if (!aliasName.trim() || !aliasValue) { | |||
return; | |||
} | |||
unitAliases[aliasName.trim()] = aliasValue; | |||
}); | |||
stylesheetFormatter['unitAliases'] = unitAliases; | |||
break; | |||
case `${syntax}.valueSeparator`: | |||
stylesheetFormatter['between'] = preferences[key]; | |||
break; | |||
case `${syntax}.propertyEnd`: | |||
stylesheetFormatter['after'] = preferences[key]; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
return { | |||
stylesheet: stylesheetFormatter | |||
}; | |||
} | |||
/** | |||
* Updates customizations from snippets.json and syntaxProfiles.json files in the directory configured in emmet.extensionsPath setting | |||
*/ | |||
function updateExtensionsPath(emmetExtensionsPath, workspaceFolderPath) { | |||
if (!emmetExtensionsPath || !emmetExtensionsPath.trim()) { | |||
resetSettingsFromFile(); | |||
return Promise.resolve(); | |||
} | |||
emmetExtensionsPath = emmetExtensionsPath.trim(); | |||
workspaceFolderPath = workspaceFolderPath ? workspaceFolderPath.trim() : ''; | |||
if (emmetExtensionsPath[0] === '~') { | |||
emmetExtensionsPath = path.join(os_1.homedir(), emmetExtensionsPath.substr(1)); | |||
} | |||
else if (!path.isAbsolute(emmetExtensionsPath) && workspaceFolderPath) { | |||
emmetExtensionsPath = path.join(workspaceFolderPath, emmetExtensionsPath); | |||
} | |||
if (!path.isAbsolute(emmetExtensionsPath)) { | |||
resetSettingsFromFile(); | |||
return Promise.reject('The path provided in emmet.extensionsPath setting should be absoulte path'); | |||
} | |||
if (!dirExists(emmetExtensionsPath)) { | |||
resetSettingsFromFile(); | |||
return Promise.reject(`The directory ${emmetExtensionsPath} doesnt exist. Update emmet.extensionsPath setting`); | |||
} | |||
let dirPath = emmetExtensionsPath; | |||
let snippetsPath = path.join(dirPath, 'snippets.json'); | |||
let profilesPath = path.join(dirPath, 'syntaxProfiles.json'); | |||
let snippetsPromise = new Promise((resolve, reject) => { | |||
fs.readFile(snippetsPath, (err, snippetsData) => { | |||
if (err) { | |||
return reject(`Error while fetching the file ${snippetsPath}`); | |||
} | |||
try { | |||
let errors = []; | |||
let snippetsJson = JSONC.parse(snippetsData.toString(), errors); | |||
if (errors.length > 0) { | |||
return reject(`Found error ${JSONC.ScanError[errors[0].error]} while parsing the file ${snippetsPath} at offset ${errors[0].offset}`); | |||
} | |||
variablesFromFile = snippetsJson['variables']; | |||
customSnippetRegistry = {}; | |||
snippetKeyCache.clear(); | |||
Object.keys(snippetsJson).forEach(syntax => { | |||
if (!snippetsJson[syntax]['snippets']) { | |||
return; | |||
} | |||
let baseSyntax = isStyleSheet(syntax) ? 'css' : 'html'; | |||
let customSnippets = snippetsJson[syntax]['snippets']; | |||
if (snippetsJson[baseSyntax] && snippetsJson[baseSyntax]['snippets'] && baseSyntax !== syntax) { | |||
customSnippets = Object.assign({}, snippetsJson[baseSyntax]['snippets'], snippetsJson[syntax]['snippets']); | |||
} | |||
if (!isStyleSheet(syntax)) { | |||
// In Emmet 2.0 all snippets should be valid abbreviations | |||
// Convert old snippets that do not follow this format to new format | |||
for (let snippetKey in customSnippets) { | |||
if (customSnippets.hasOwnProperty(snippetKey) | |||
&& customSnippets[snippetKey].startsWith('<') | |||
&& customSnippets[snippetKey].endsWith('>')) { | |||
customSnippets[snippetKey] = `{${customSnippets[snippetKey]}}`; | |||
} | |||
} | |||
} | |||
else { | |||
stylesheetCustomSnippetsKeyCache.set(syntax, Object.keys(customSnippets)); | |||
} | |||
customSnippetRegistry[syntax] = expand_full_1.createSnippetsRegistry(syntax, customSnippets); | |||
let snippetKeys = customSnippetRegistry[syntax].all({ type: 'string' }).map(snippet => { | |||
return snippet.key; | |||
}); | |||
snippetKeyCache.set(syntax, snippetKeys); | |||
}); | |||
} | |||
catch (e) { | |||
return reject(`Error while parsing the file ${snippetsPath}`); | |||
} | |||
return resolve(); | |||
}); | |||
}); | |||
let variablesPromise = new Promise((resolve, reject) => { | |||
fs.readFile(profilesPath, (err, profilesData) => { | |||
try { | |||
if (!err) { | |||
profilesFromFile = JSON.parse(profilesData.toString()); | |||
} | |||
} | |||
catch (e) { | |||
} | |||
return resolve(); | |||
}); | |||
}); | |||
return Promise.all([snippetsPromise, variablesFromFile]).then(() => Promise.resolve()); | |||
} | |||
exports.updateExtensionsPath = updateExtensionsPath; | |||
function dirExists(dirPath) { | |||
try { | |||
return fs.statSync(dirPath).isDirectory(); | |||
} | |||
catch (e) { | |||
return false; | |||
} | |||
} | |||
function resetSettingsFromFile() { | |||
customSnippetRegistry = {}; | |||
snippetKeyCache.clear(); | |||
stylesheetCustomSnippetsKeyCache.clear(); | |||
profilesFromFile = {}; | |||
variablesFromFile = {}; | |||
} | |||
/** | |||
* Get the corresponding emmet mode for given vscode language mode | |||
* Eg: jsx for typescriptreact/javascriptreact or pug for jade | |||
* If the language is not supported by emmet or has been exlcuded via `exlcudeLanguages` setting, | |||
* then nothing is returned | |||
* | |||
* @param language | |||
* @param exlcudedLanguages Array of language ids that user has chosen to exlcude for emmet | |||
*/ | |||
function getEmmetMode(language, excludedLanguages = []) { | |||
if (!language || excludedLanguages.indexOf(language) > -1) { | |||
return; | |||
} | |||
if (/\b(typescriptreact|javascriptreact|jsx-tags)\b/.test(language)) { // treat tsx like jsx | |||
return 'jsx'; | |||
} | |||
if (language === 'sass-indented') { // map sass-indented to sass | |||
return 'sass'; | |||
} | |||
if (language === 'jade') { | |||
return 'pug'; | |||
} | |||
if (emmetModes.indexOf(language) > -1) { | |||
return language; | |||
} | |||
} | |||
exports.getEmmetMode = getEmmetMode; | |||
const propertyHexColorRegex = /^[a-zA-Z]+:?#[\d.a-fA-F]{0,6}$/; | |||
const hexColorRegex = /^#[\d,a-f,A-F]{1,6}$/; | |||
const onlyLetters = /^[a-z,A-Z]+$/; | |||
/** | |||
* Returns a completion participant for Emmet of the form { | |||
* onCssProperty: () => void | |||
* onCssPropertyValue: () => void | |||
* onHtmlContent: () => void | |||
* } | |||
* @param document The TextDocument for which completions are being provided | |||
* @param position The Position in the given document where completions are being provided | |||
* @param syntax The Emmet syntax to use when providing Emmet completions | |||
* @param emmetSettings The Emmet settings to use when providing Emmet completions | |||
* @param result The Completion List object that needs to be updated with Emmet completions | |||
*/ | |||
function getEmmetCompletionParticipants(document, position, syntax, emmetSettings, result) { | |||
return { | |||
getId: () => 'emmet', | |||
onCssProperty: (context) => { | |||
if (context && context.propertyName) { | |||
const currentresult = doComplete(document, position, syntax, emmetSettings); | |||
if (result && currentresult) { | |||
result.items = currentresult.items; | |||
result.isIncomplete = true; | |||
} | |||
} | |||
}, | |||
onCssPropertyValue: (context) => { | |||
if (context && context.propertyValue) { | |||
const extractedResults = extractAbbreviation(document, position, { syntax: 'css', lookAhead: false }); | |||
if (!extractedResults) { | |||
return; | |||
} | |||
const validAbbreviationWithColon = extractedResults.abbreviation === `${context.propertyName}:${context.propertyValue}` && onlyLetters.test(context.propertyValue); | |||
if (validAbbreviationWithColon // Allows abbreviations like pos:f | |||
|| hexColorRegex.test(extractedResults.abbreviation) | |||
|| extractedResults.abbreviation === '!') { | |||
const currentresult = doComplete(document, position, syntax, emmetSettings); | |||
if (result && currentresult) { | |||
result.items = currentresult.items; | |||
result.isIncomplete = true; | |||
} | |||
} | |||
} | |||
}, | |||
onHtmlContent: () => { | |||
const currentresult = doComplete(document, position, syntax, emmetSettings); | |||
if (result && currentresult) { | |||
result.items = currentresult.items; | |||
result.isIncomplete = true; | |||
} | |||
} | |||
}; | |||
} | |||
exports.getEmmetCompletionParticipants = getEmmetCompletionParticipants; | |||
//# sourceMappingURL=emmetHelper.js.map |
@@ -1,61 +0,0 @@ | |||
{ | |||
"_from": "vscode-emmet-helper@^1.2.15", | |||
"_id": "vscode-emmet-helper@1.2.17", | |||
"_inBundle": false, | |||
"_integrity": "sha512-X4pzcrJ8dE7M3ArFuySF5fgipKDd/EauXkiJwtjBIVRWpVNq0tF9+lNCyuC7iDUwP3Oq7ow/TGssD3GdG96Jow==", | |||
"_location": "/vscode-emmet-helper", | |||
"_phantomChildren": {}, | |||
"_requested": { | |||
"type": "range", | |||
"registry": true, | |||
"raw": "vscode-emmet-helper@^1.2.15", | |||
"name": "vscode-emmet-helper", | |||
"escapedName": "vscode-emmet-helper", | |||
"rawSpec": "^1.2.15", | |||
"saveSpec": null, | |||
"fetchSpec": "^1.2.15" | |||
}, | |||
"_requiredBy": [ | |||
"/" | |||
], | |||
"_resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-1.2.17.tgz", | |||
"_shasum": "f0c6bfcebc4285d081fb2618e6e5b9a08c567afa", | |||
"_spec": "vscode-emmet-helper@^1.2.15", | |||
"_where": "/tmp/coc-emmet-I4j5XV", | |||
"author": { | |||
"name": "Microsoft Corporation" | |||
}, | |||
"bugs": { | |||
"url": "https://github.com/Microsoft/vscode-emmet-helper" | |||
}, | |||
"bundleDependencies": false, | |||
"dependencies": { | |||
"@emmetio/extract-abbreviation": "0.1.6", | |||
"jsonc-parser": "^1.0.0", | |||
"vscode-languageserver-types": "^3.6.0-next.1" | |||
}, | |||
"deprecated": false, | |||
"description": "Helper to use emmet modules in Visual Studio Code", | |||
"devDependencies": { | |||
"@types/node": "^6.0.46", | |||
"cpy-cli": "^1.0.1", | |||
"mocha": "3.4.2", | |||
"typescript": "^2.1.5" | |||
}, | |||
"homepage": "https://github.com/Microsoft/vscode-emmet-helper#readme", | |||
"license": "MIT", | |||
"main": "./out/emmetHelper.js", | |||
"name": "vscode-emmet-helper", | |||
"repository": { | |||
"type": "git", | |||
"url": "git+https://github.com/Microsoft/vscode-emmet-helper.git" | |||
}, | |||
"scripts": { | |||
"compile": "tsc -p ./ && cpy ./src/expand/*.js ./out/expand && cpy ./src/typings/emmetHelper.d.ts ./out", | |||
"prepublish": "npm run compile && npm run test", | |||
"test": "mocha out/test", | |||
"watch": "tsc -watch -p ./" | |||
}, | |||
"types": "./out/emmetHelper.d.ts", | |||
"version": "1.2.17" | |||
} |
@@ -1,53 +0,0 @@ | |||
THIRD-PARTY SOFTWARE NOTICES AND INFORMATION | |||
vscode-emmet-helper incorporates third party material from the projects listed below. The original copyright | |||
notice and the license under which Microsoft received such third party material are set forth below. Microsoft | |||
reserves all other rights not expressly granted, whether by implication, estoppel or otherwise. | |||
1. expand-abbreivation (https://github.com/emmetio/expand-abbreivation) | |||
MIT License | |||
Copyright (c) 2017 Emmet.io | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. | |||
2. extract-abbreivation (https://github.com/emmetio/extract-abbreivation) | |||
MIT License | |||
Copyright (c) 2017 Emmet.io | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. |
@@ -1,11 +0,0 @@ | |||
{ | |||
"compilerOptions": { | |||
"target": "es6", | |||
"lib": ["es2016"], | |||
"module": "commonjs", | |||
"outDir": "out", | |||
"sourceMap": true, | |||
"allowJs": true | |||
}, | |||
"exclude": ["node_modules", "out"] | |||
} |
@@ -1 +0,0 @@ | |||
lib |
@@ -1,11 +0,0 @@ | |||
Copyright (c) Microsoft Corporation | |||
All rights reserved. | |||
MIT License | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@@ -1,17 +0,0 @@ | |||
# VSCode Language Server Types | |||
[![NPM Version](https://img.shields.io/npm/v/vscode-languageserver-types.svg)](https://npmjs.org/package/vscode-languageserver-types) | |||
[![NPM Downloads](https://img.shields.io/npm/dm/vscode-languageserver-types.svg)](https://npmjs.org/package/vscode-languageserver-types) | |||
[![Build Status](https://travis-ci.org/Microsoft/vscode-languageserver-types-node.svg?branch=master)](https://travis-ci.org/Microsoft/vscode-languageserver-types-node) | |||
Npm module containing the types used by the VSCode language client and [Node.js](https://nodejs.org/) language server | |||
Click [here](https://code.visualstudio.com/docs/extensions/example-language-server) for a detailed document on how | |||
to implement language servers for [VSCode](https://code.visualstudio.com/). | |||
## History | |||
For the history please see the [main repository](https://github.com/Microsoft/vscode-languageserver-node/blob/master/README.md) | |||
## License | |||
[MIT](https://github.com/Microsoft/vscode-languageserver-node/blob/master/License.txt) |
@@ -1,58 +0,0 @@ | |||
{ | |||
"_from": "vscode-languageserver-types@^3.6.0-next.1", | |||
"_id": "vscode-languageserver-types@3.15.1", | |||
"_inBundle": false, | |||
"_integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==", | |||
"_location": "/vscode-languageserver-types", | |||
"_phantomChildren": {}, | |||
"_requested": { | |||
"type": "range", | |||
"registry": true, | |||
"raw": "vscode-languageserver-types@^3.6.0-next.1", | |||
"name": "vscode-languageserver-types", | |||
"escapedName": "vscode-languageserver-types", | |||
"rawSpec": "^3.6.0-next.1", | |||
"saveSpec": null, | |||
"fetchSpec": "^3.6.0-next.1" | |||
}, | |||
"_requiredBy": [ | |||
"/coc.nvim", | |||
"/vscode-emmet-helper", | |||
"/vscode-languageserver-protocol" | |||
], | |||
"_resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz", | |||
"_shasum": "17be71d78d2f6236d414f0001ce1ef4d23e6b6de", | |||
"_spec": "vscode-languageserver-types@^3.6.0-next.1", | |||
"_where": "/tmp/coc-emmet-I4j5XV/node_modules/vscode-emmet-helper", | |||
"author": { | |||
"name": "Microsoft Corporation" | |||
}, | |||
"bugs": { | |||
"url": "https://github.com/Microsoft/vscode-languageserver-node/issues" | |||
}, | |||
"bundleDependencies": false, | |||
"deprecated": false, | |||
"description": "Types used by the Language server for node", | |||
"homepage": "https://github.com/Microsoft/vscode-languageserver-node#readme", | |||
"license": "MIT", | |||
"main": "./lib/umd/main.js", | |||
"module": "./lib/esm/main.js", | |||
"name": "vscode-languageserver-types", | |||
"repository": { | |||
"type": "git", | |||
"url": "git+https://github.com/Microsoft/vscode-languageserver-node.git", | |||
"directory": "types" | |||
}, | |||
"scripts": { | |||
"clean": "node ../node_modules/rimraf/bin.js lib", | |||
"compile": "node ../build/bin/tsc -b ./tsconfig.json", | |||
"compile-esm": "node ../build/bin/tsc -b ./tsconfig.esm.json", | |||
"postpublish": "node ../build/npm/post-publish.js", | |||
"prepublishOnly": "npm run clean && npm run compile-esm && npm run compile && npm run test", | |||
"preversion": "npm test", | |||
"test": "node ../node_modules/mocha/bin/_mocha", | |||
"watch": "node ../build/bin/tsc -b ./tsconfig.json -w" | |||
}, | |||
"typings": "./lib/umd/main", | |||
"version": "3.15.1" | |||
} |
@@ -1,18 +0,0 @@ | |||
{ | |||
"extends": "../tsconfig.base.json", | |||
"compilerOptions": { | |||
"target": "es5", | |||
"module": "es6", | |||
"sourceMap": false, | |||
"declaration": true, | |||
"stripInternal": true, | |||
"lib": [ "es2015" ], | |||
"outDir": "lib/esm", | |||
"incremental": true, | |||
"tsBuildInfoFile":"./lib/tsconfig.esm.tsbuildInfo", | |||
"rootDir": "src" | |||
}, | |||
"include": [ | |||
"src" | |||
] | |||
} |
@@ -1,204 +0,0 @@ | |||
{ | |||
"name": "coc-emmet", | |||
"version": "1.1.4", | |||
"description": "emmet extension for coc", | |||
"main": "lib/index.js", | |||
"publisher": "chemzqm", | |||
"keywords": [ | |||
"coc.nvim", | |||
"emmet", | |||
"html", | |||
"css" | |||
], | |||
"engines": { | |||
"coc": "^0.0.29" | |||
}, | |||
"scripts": { | |||
"clean": "rimraf lib", | |||
"build": "webpack", | |||
"prepare": "npx npm-run-all clean build" | |||
}, | |||
"activationEvents": [ | |||
"*" | |||
], | |||
"contributes": { | |||
"configuration": { | |||
"type": "object", | |||
"title": "Emmet", | |||
"properties": { | |||
"emmet.showExpandedAbbreviation": { | |||
"type": [ | |||
"string" | |||
], | |||
"enum": [ | |||
"never", | |||
"always", | |||
"inMarkupAndStylesheetFilesOnly" | |||
], | |||
"default": "always", | |||
"markdownDescription": "Shows expanded Emmet abbreviations as suggestions." | |||
}, | |||
"emmet.showAbbreviationSuggestions": { | |||
"type": "boolean", | |||
"default": true, | |||
"markdownDescription": "Shows possible Emmet abbreviations as suggestions. Not applicable in stylesheets or when emmet.showExpandedAbbreviation is 'never'." | |||
}, | |||
"emmet.includeLanguages": { | |||
"type": "object", | |||
"default": {}, | |||
"markdownDescription": "Enable Emmet abbreviations in languages that are not supported by default. Add a mapping here between the language and emmet supported language. E.g.: `{\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}`" | |||
}, | |||
"emmet.variables": { | |||
"type": "object", | |||
"properties": { | |||
"lang": { | |||
"type": "string", | |||
"default": "en" | |||
}, | |||
"charset": { | |||
"type": "string", | |||
"default": "UTF-8" | |||
} | |||
}, | |||
"default": {}, | |||
"description": "Variables to be used in Emmet snippets" | |||
}, | |||
"emmet.syntaxProfiles": { | |||
"type": "object", | |||
"default": {}, | |||
"description": "Define profile for specified syntax or use your own profile with specific rules." | |||
}, | |||
"emmet.excludeLanguages": { | |||
"type": "array", | |||
"default": [ | |||
"markdown" | |||
], | |||
"description": "An array of languages where Emmet abbreviations should not be expanded." | |||
}, | |||
"emmet.extensionsPath": { | |||
"type": "string", | |||
"default": null, | |||
"description": "Path to a folder containing Emmet profiles and snippets." | |||
}, | |||
"emmet.showSuggestionsAsSnippets": { | |||
"type": "boolean", | |||
"default": true, | |||
"markdownDescription": "Show emmet completion items as snippet kind." | |||
}, | |||
"emmet.preferences": { | |||
"type": "object", | |||
"default": {}, | |||
"description": "Preferences used to modify behavior of some actions and resolvers of Emmet.", | |||
"properties": { | |||
"css.intUnit": { | |||
"type": "string", | |||
"default": "px" | |||
}, | |||
"css.floatUnit": { | |||
"type": "string", | |||
"default": "em" | |||
}, | |||
"css.propertyEnd": { | |||
"type": "string", | |||
"default": ";" | |||
}, | |||
"sass.propertyEnd": { | |||
"type": "string", | |||
"default": "" | |||
}, | |||
"stylus.propertyEnd": { | |||
"type": "string", | |||
"default": "" | |||
}, | |||
"css.valueSeparator": { | |||
"type": "string", | |||
"default": ": " | |||
}, | |||
"sass.valueSeparator": { | |||
"type": "string", | |||
"default": ": " | |||
}, | |||
"stylus.valueSeparator": { | |||
"type": "string", | |||
"default": " " | |||
}, | |||
"bem.elementSeparator": { | |||
"type": "string", | |||
"default": "__" | |||
}, | |||
"bem.modifierSeparator": { | |||
"type": "string", | |||
"default": "_" | |||
}, | |||
"filter.commentBefore": { | |||
"type": "string", | |||
"default": "" | |||
}, | |||
"filter.commentAfter": { | |||
"type": "string", | |||
"default": "\n<!-- /[#ID][.CLASS] -->" | |||
}, | |||
"filter.commentTrigger": { | |||
"type": "array", | |||
"default": [ | |||
"id", | |||
"class" | |||
] | |||
}, | |||
"format.noIndentTags": { | |||
"type": "array", | |||
"default": [ | |||
"html" | |||
] | |||
}, | |||
"format.forceIndentationForTags": { | |||
"type": "array", | |||
"default": [ | |||
"body" | |||
] | |||
}, | |||
"profile.allowCompactBoolean": { | |||
"type": "boolean", | |||
"default": false | |||
}, | |||
"css.fuzzySearchMinScore": { | |||
"type": "number", | |||
"default": 0.3 | |||
} | |||
} | |||
}, | |||
"emmet.optimizeStylesheetParsing": { | |||
"type": "boolean", | |||
"default": true, | |||
"markdownDescription": "When set to `false`, the whole file is parsed to determine if current position is valid for expanding Emmet abbreviations. When set to `true`, only the content around the current position in css/scss/less files is parsed." | |||
}, | |||
"emmet.priority": { | |||
"type": "integer", | |||
"default": 3 | |||
} | |||
} | |||
} | |||
}, | |||
"author": "chemzqm@gmail.com", | |||
"license": "MIT", | |||
"devDependencies": { | |||
"@chemzqm/tsconfig": "^0.0.3", | |||
"@chemzqm/tslint-config": "^1.0.18", | |||
"@types/node": "^10.14.7", | |||
"coc.nvim": "^0.0.67", | |||
"rimraf": "^2.6.3", | |||
"ts-loader": "^6.0.3", | |||
"tslint": "^5.16.0", | |||
"typescript": "^3.4.4", | |||
"webpack": "^4.34.0", | |||
"webpack-cli": "^3.3.4", | |||
"@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", | |||
"@emmetio/html-matcher": "^0.3.3", | |||
"@emmetio/math-expression": "^0.1.1", | |||
"vscode-languageserver-protocol": "^3.15.0-next.5", | |||
"vscode-uri": "^1.0.6" | |||
}, | |||
"dependencies": { | |||
"vscode-emmet-helper": "^1.2.15" | |||
} | |||
} |
@@ -1,40 +0,0 @@ | |||
const path = require('path') | |||
module.exports = { | |||
entry: './src/index.ts', | |||
target: 'node', | |||
mode: 'none', | |||
resolve: { | |||
mainFields: ['module', 'main'], | |||
extensions: ['.js', '.ts'] | |||
}, | |||
externals: { | |||
'coc.nvim': 'commonjs coc.nvim', | |||
'vscode-emmet-helper': 'commonjs vscode-emmet-helper', | |||
}, | |||
module: { | |||
rules: [{ | |||
test: /\.ts$/, | |||
exclude: /node_modules/, | |||
use: [{ | |||
loader: 'ts-loader', | |||
options: { | |||
compilerOptions: { | |||
"sourceMap": true, | |||
} | |||
} | |||
}] | |||
}] | |||
}, | |||
output: { | |||
path: path.join(__dirname, 'lib'), | |||
filename: 'index.js', | |||
libraryTarget: "commonjs", | |||
}, | |||
plugins: [ | |||
], | |||
node: { | |||
__dirname: false, | |||
__filename: false | |||
} | |||
} |
@@ -1,63 +0,0 @@ | |||
# coc-json | |||
Json language server extension for [coc.nvim](https://github.com/neoclide/coc.nvim). | |||
The server code is extracted from VSCode, which uses | |||
[vscode-json-languageservice](https://www.npmjs.com/package/vscode-json-languageservice) | |||
## Install | |||
In your vim/neovim, run the following command: | |||
``` | |||
:CocInstall coc-json | |||
``` | |||
## Features | |||
Same as VSCode. | |||
All features of [vscode-json-languageservice](https://www.npmjs.com/package/vscode-json-languageservice) are supported. | |||
- `doCompletion` for JSON properties and values based on the document's JSON schema. | |||
- `doHover` for values based on descriptions in the document's JSON schema.<Paste> | |||
- Document Symbols for quick navigation to properties in the document. | |||
- Document Colors for showing color decorators on values representing colors. | |||
- Code Formatting supporting ranges and formatting the whole document. | |||
- Diagnostics (Validation) are pushed for all open documents | |||
- syntax errors | |||
- structural validation based on the document's JSON schema. | |||
## Commands | |||
- `json.retryResolveSchema`: Retry resolve schema of current buffer. | |||
## Configuration options | |||
- `json.enable` set to `false` to disable json language server. | |||
- `json.trace.server` trace LSP traffic in output channel. | |||
- `json.execArgv` add `execArgv` to `child_process.fork` used for start | |||
json language server. | |||
- `json.format.enable` set to `false` to disable format. | |||
- `json.schemas` schema associations for json files. | |||
## FAQ | |||
### How to suppress error `[json 521] [e] Comments are not permitted in JSON`? | |||
You can configure your vim to make that file with jsonc filetype to allow comment. | |||
### How to add custom schema definitions/properties? | |||
You have two choices: | |||
- use `$schema` in your json. | |||
- create json schema file and then configure `json.schemes` in your `coc-settings.json`, check out https://github.com/neoclide/coc-json/blob/master/package.json#L55 | |||
### Quotes are hidden? | |||
This is not caused by coc-json, you may checkout the `conceallevel` option. | |||
## License | |||
MIT |
@@ -1,271 +0,0 @@ | |||
{ | |||
"$schema": "http://json-schema.org/draft-04/schema", | |||
"description": "微信小程序 app.json 的 schema", | |||
"type": "object", | |||
"definitions": { | |||
"pages": { | |||
"type": "array", | |||
"items": { | |||
"type": "string", | |||
"description": "页面路径" | |||
}, | |||
"uniqueItems": true, | |||
"description": "每一项都是字符串,来指定小程序由哪些页面组成,数组的第一项代表小程序首页" | |||
} | |||
}, | |||
"properties": { | |||
"pages": { | |||
"$ref": "#/definitions/pages" | |||
}, | |||
"window": { | |||
"type": "object", | |||
"properties": { | |||
"navigationBarBackgroundColor": { | |||
"type": "string", | |||
"default": "#000000", | |||
"description": "导航栏背景颜色,HexColor" | |||
}, | |||
"navigationBarTextStyle": { | |||
"type": "string", | |||
"description": "导航栏标题颜色,仅支持 black/white", | |||
"default": "white", | |||
"enum": [ | |||
"white", | |||
"black" | |||
] | |||
}, | |||
"navigationBarTitleText": { | |||
"type": "string", | |||
"description": "导航栏标题文字内容" | |||
}, | |||
"navigationStyle": { | |||
"type": "string", | |||
"enum": [ | |||
"default", | |||
"custom" | |||
], | |||
"default": "default", | |||
"description": "导航栏样式,微信客户端 6.6.0" | |||
}, | |||
"backgroundColor": { | |||
"type": "string", | |||
"default": "#ffffff", | |||
"description": "窗口的背景色, HexColor" | |||
}, | |||
"backgroundTextStyle": { | |||
"type": "string", | |||
"default": "dark", | |||
"enum": [ | |||
"dark", | |||
"light" | |||
], | |||
"description": "下拉背景字体、loading 图的样式,仅支持 dark/light" | |||
}, | |||
"backgroundColorTop": { | |||
"type": "string", | |||
"description": "顶部窗口的背景色,仅 iOS 支持,微信客户端 6.5.16", | |||
"default": "#ffffff" | |||
}, | |||
"backgroundColorBottom": { | |||
"type": "string", | |||
"description": "底部窗口的背景色,仅 iOS 支持微信客户端 6.5.16", | |||
"default": "#ffffff" | |||
}, | |||
"enablePullDownRefresh": { | |||
"type": "boolean", | |||
"default": false, | |||
"description": "是否开启下拉刷新" | |||
}, | |||
"onReachBottomDistance": { | |||
"type": "number", | |||
"default": 50, | |||
"description": "页面上拉触底事件触发时距页面底部距离,单位为px" | |||
}, | |||
"pageOrientation": { | |||
"type": "string", | |||
"description": "屏幕旋转设置,2.4.0 (auto) / 2.5.0 (landscape)", | |||
"default": "portrait", | |||
"enum": [ | |||
"auto", | |||
"portrait", | |||
"landscape" | |||
] | |||
} | |||
}, | |||
"description": "全局的默认窗口表现" | |||
}, | |||
"tabBar": { | |||
"type": "object", | |||
"required": [ | |||
"color", | |||
"selectedColor", | |||
"backgroundColor", | |||
"list" | |||
], | |||
"default": { | |||
"color": "", | |||
"selectedColor": "", | |||
"backgroundColor": "", | |||
"list": [] | |||
}, | |||
"properties": { | |||
"color": { | |||
"type": "string", | |||
"description": "tab 上的文字默认颜色" | |||
}, | |||
"selectedColor": { | |||
"type": "string", | |||
"description": "tab 上的文字选中时的颜色" | |||
}, | |||
"backgroundColor": { | |||
"type": "string", | |||
"description": "tab 的背景色" | |||
}, | |||
"borderStyle": { | |||
"type": "string", | |||
"enum": [ | |||
"black", | |||
"white" | |||
], | |||
"default": "black", | |||
"description": "tabbar上边框的颜色, 仅支持 black/white" | |||
}, | |||
"position": { | |||
"type": "string", | |||
"default": "bottom", | |||
"enum": [ | |||
"bottom", | |||
"top" | |||
], | |||
"description": "可选值 bottom、top" | |||
}, | |||
"list": { | |||
"type": "array", | |||
"items": { | |||
"type": "object", | |||
"required": [ | |||
"pagePath", | |||
"text" | |||
], | |||
"properties": { | |||
"pagePath": { | |||
"type": "string", | |||
"description": "页面路径,必须在 pages 中先定义" | |||
}, | |||
"text": { | |||
"type": "string", | |||
"description": "tab 上按钮文字" | |||
}, | |||
"iconPath": { | |||
"type": "string", | |||
"description": "图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效,不支持网络图片" | |||
}, | |||
"selectedIconPath": { | |||
"type": "string", | |||
"description": "选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 postion 为 top 时,此参数无效" | |||
} | |||
} | |||
}, | |||
"minItems": 2, | |||
"maxItems": 5 | |||
} | |||
}, | |||
"description": "底部 tab 栏的表现" | |||
}, | |||
"networkTimeout": { | |||
"type": "object", | |||
"properties": { | |||
"request": { | |||
"type": "number", | |||
"default": 60000, | |||
"description": "wx.request的超时时间,单位毫秒,默认为:60000" | |||
}, | |||
"connectSocket": { | |||
"type": "number", | |||
"default": 60000, | |||
"description": "wx.connectSocket的超时时间,单位毫秒,默认为:60000" | |||
}, | |||
"uploadFile": { | |||
"type": "number", | |||
"default": 60000, | |||
"description": "wx.uploadFile的超时时间,单位毫秒,默认为:60000" | |||
}, | |||
"downloadFile": { | |||
"type": "number", | |||
"default": 60000, | |||
"description": "wx.downloadFile的超时时间,单位毫秒,默认为:60000" | |||
} | |||
}, | |||
"description": "网络超时时间" | |||
}, | |||
"debug": { | |||
"type": "boolean", | |||
"description": "可以在开发者工具中开启 debug 模式,在开发者工具的控制台面板,调试信息以 info 的形式给出,其信息有Page的注册,页面路由,数据更新,事件触发 。 可以帮助开发者快速定位一些常见的问题" | |||
}, | |||
"functionalPages": { | |||
"type": "boolean", | |||
"description": "是否启用插件功能页", | |||
"default": false | |||
}, | |||
"subPackages": { | |||
"type": "array", | |||
"description": "分包加载", | |||
"items": { | |||
"type": "object", | |||
"properties": { | |||
"root": { | |||
"type": "string", | |||
"description": "指定分包的名称,对应会自动生成一个文件夹" | |||
}, | |||
"pages": { | |||
"$ref": "#/definitions/pages" | |||
} | |||
} | |||
} | |||
}, | |||
"requiredBackgroundModes": { | |||
"type": "array", | |||
"items": { | |||
"type": "string", | |||
"description": "台运行的能力", | |||
"enum": [ | |||
"audio" | |||
] | |||
}, | |||
"description": "申明需要后台运行的能力,类型为数组", | |||
"uniqueItems": true | |||
}, | |||
"plugins": { | |||
"type": "object", | |||
"default": {}, | |||
"description": "引入插件代码包", | |||
"properties": {} | |||
}, | |||
"resizable": { | |||
"type": "boolean", | |||
"description": "iPad 小程序是否支持屏幕旋转", | |||
"default": false | |||
}, | |||
"navigateToMiniProgramAppIdList": { | |||
"type": "array", | |||
"items": { | |||
"type": "string", | |||
"description": "对应appid" | |||
}, | |||
"description": "需要跳转的小程序列表" | |||
}, | |||
"usingComponents": { | |||
"type": "object", | |||
"properties": {}, | |||
"description": "全局自定义组件配置" | |||
}, | |||
"permission": { | |||
"type": "object", | |||
"properties": {}, | |||
"description": "小程序接口权限相关设置,微信客户端 7.0.0" | |||
} | |||
}, | |||
"required": [ | |||
"pages" | |||
] | |||
} |
@@ -1,83 +0,0 @@ | |||
{ | |||
"$schema": "http://json-schema.org/draft-04/schema", | |||
"description": "微信小程序 自定义组件 的 schema", | |||
"type": "object", | |||
"required": ["component"], | |||
"properties": { | |||
"component": { | |||
"type": "boolean", | |||
"default": true | |||
}, | |||
"usingComponents": { | |||
"type": "object" | |||
}, | |||
"minapp": { | |||
"type": "object", | |||
"properties": { | |||
"component": { | |||
"type": "object", | |||
"description": "关于此组件的文档", | |||
"properties": { | |||
"docLink": { | |||
"type": "string", | |||
"description": "线上文档链接" | |||
}, | |||
"desc": { | |||
"type": "array", | |||
"description": "组件的描述,支持 markdown 语法", | |||
"items": { | |||
"type": "string" | |||
} | |||
}, | |||
"attrs": { | |||
"type": "array", | |||
"description": "组件属性列表", | |||
"items": { | |||
"$ref": "#/definitions/attr" | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
"definitions": { | |||
"attr": { | |||
"required": ["name", "type"], | |||
"type": "object", | |||
"properties": { | |||
"name": { | |||
"type": "string", | |||
"description": "属性名字" | |||
}, | |||
"type": { | |||
"type": "object", | |||
"description": "属性类型", | |||
"required": ["name"], | |||
"default": {"name": "string"}, | |||
"properties": { | |||
"name": { | |||
"type": "string" | |||
} | |||
} | |||
}, | |||
"desc": { | |||
"type": "array", | |||
"description": "属性的描述,支持 markdown 语法", | |||
"items": { | |||
"type": "string" | |||
} | |||
}, | |||
"required": { | |||
"type": "boolean", | |||
"description": "是否必填" | |||
}, | |||
"defaultValue": { | |||
"description": "属性默认值" | |||
} | |||
} | |||
} | |||
} | |||
} | |||
@@ -1,70 +0,0 @@ | |||
{ | |||
"$schema": "http://json-schema.org/draft-04/schema", | |||
"description": "微信小程序 page.json 的 schema", | |||
"type": "object", | |||
"properties": { | |||
"usingComponents": { | |||
"type": "object" | |||
}, | |||
"navigationBarBackgroundColor": { | |||
"type": "string", | |||
"default": "#000000", | |||
"description": "导航栏背景颜色,HexColor" | |||
}, | |||
"navigationBarTextStyle": { | |||
"type": "string", | |||
"description": "导航栏标题颜色,仅支持 black/white", | |||
"default": "white", | |||
"enum": [ | |||
"white", "black" | |||
] | |||
}, | |||
"navigationBarTitleText": { | |||
"type": "string", | |||
"description": "导航栏标题文字内容" | |||
}, | |||
"backgroundColor": { | |||
"type": "string", | |||
"default": "#ffffff", | |||
"description": "窗口的背景色, HexColor" | |||
}, | |||
"backgroundTextStyle": { | |||
"type": "string", | |||
"default": "dark", | |||
"enum": [ | |||
"dark", "light" | |||
], | |||
"description": "下拉背景字体、loading 图的样式,仅支持 dark/light" | |||
}, | |||
"enablePullDownRefresh": { | |||
"type": "boolean", | |||
"default": false, | |||
"description": "是否开启下拉刷新" | |||
}, | |||
"onReachBottomDistance": { | |||
"type": "number", | |||
"default": 50, | |||
"description": "页面上拉触底事件触发时距页面底部距离,单位为px" | |||
}, | |||
"pageOrientation": { | |||
"type": "string", | |||
"description": "屏幕旋转设置,2.4.0 (auto) / 2.5.0 (landscape)", | |||
"default": "portrait", | |||
"enum": [ | |||
"auto", | |||
"portrait", | |||
"landscape" | |||
] | |||
}, | |||
"disableScroll": { | |||
"type": "boolean", | |||
"description": "设置为 true 则页面整体不能上下滚动。 只在页面配置中有效", | |||
"default": false | |||
}, | |||
"disableSwipeBack": { | |||
"type": "boolean", | |||
"description": "禁止页面右滑手势返回,微信客户端 7.0.0", | |||
"default": false | |||
} | |||
} | |||
} |
@@ -1,111 +0,0 @@ | |||
{ | |||
"name": "coc-json", | |||
"version": "1.2.6", | |||
"description": "Json extension for coc.nvim", | |||
"main": "lib/index.js", | |||
"publisher": "chemzqm", | |||
"keywords": [ | |||
"coc.nvim", | |||
"json" | |||
], | |||
"engines": { | |||
"coc": ">= 0.0.70" | |||
}, | |||
"scripts": { | |||
"clean": "rimraf lib", | |||
"watch": "webpack --watch", | |||
"build": "webpack", | |||
"prepare": "npx npm-run-all clean build" | |||
}, | |||
"activationEvents": [ | |||
"onLanguage:json", | |||
"onLanguage:jsonc" | |||
], | |||
"contributes": { | |||
"configuration": { | |||
"type": "object", | |||
"title": "Json", | |||
"properties": { | |||
"json.enable": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Enable json server" | |||
}, | |||
"json.trace.server": { | |||
"type": "string", | |||
"default": "off", | |||
"enum": [ | |||
"off", | |||
"messages", | |||
"verbose" | |||
] | |||
}, | |||
"json.execArgv": { | |||
"type": "array", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
} | |||
}, | |||
"json.format.enable": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Enable format for json server" | |||
}, | |||
"json.schemas": { | |||
"type": "array", | |||
"scope": "resource", | |||
"description": "Schemas associations for json files", | |||
"default": [], | |||
"items": { | |||
"type": "object", | |||
"default": { | |||
"fileMatch": [ | |||
"/my-file" | |||
], | |||
"url": "schemaURL" | |||
}, | |||
"properties": { | |||
"url": { | |||
"type": "string", | |||
"default": "/user.schema.json" | |||
}, | |||
"fileMatch": { | |||
"type": "array", | |||
"items": { | |||
"type": "string", | |||
"default": "MyFile.json" | |||
}, | |||
"minItems": 1, | |||
"description": "File pattern to match." | |||
}, | |||
"schema": { | |||
"$ref": "http://json-schema.org/draft-04/schema#", | |||
"description": "Url of json schema, support file/url protocol." | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
"author": "chemzqm@gmail.com", | |||
"license": "MIT", | |||
"devDependencies": { | |||
"@chemzqm/tsconfig": "^0.0.3", | |||
"@chemzqm/tslint-config": "^1.0.18", | |||
"@types/node": "^11.13.10", | |||
"coc.nvim": "^0.0.70", | |||
"request-light": "^0.2.4", | |||
"rimraf": "^2.6.3", | |||
"ts-loader": "^6.0.3", | |||
"tslint": "^5.16.0", | |||
"typescript": "^3", | |||
"vscode-json-languageservice": "3.3.0", | |||
"vscode-languageserver": "5.3.0-next.7", | |||
"vscode-uri": "2.0.1", | |||
"webpack": "^4.34.0", | |||
"webpack-cli": "^3.3.4" | |||
}, | |||
"dependencies": {} | |||
} |
@@ -1,6 +0,0 @@ | |||
#! /bin/bash | |||
curl https://raw.githubusercontent.com/qiu8310/minapp/master/schema/app.json > ./data/app.json | |||
curl https://raw.githubusercontent.com/qiu8310/minapp/master/schema/page.json > ./data/page.json | |||
curl https://raw.githubusercontent.com/qiu8310/minapp/master/schema/component.json > ./data/component.json | |||
curl https://schemastore.azurewebsites.net/api/json/catalog.json > ./src/catalog.json |
@@ -1,186 +0,0 @@ | |||
# coc-python | |||
Python extension for [coc.nvim](https://github.com/neoclide/coc.nvim), fork of | |||
[vscode-python](https://github.com/Microsoft/vscode-python) from commit | |||
[16899f6b13477786212f29eb3cb7a459b5ebf0a4](https://github.com/microsoft/vscode-python/commit/16899f6b13477786212f29eb3cb7a459b5ebf0a4). | |||
Basic working, but still **W.I.P**. | |||
Built with rich support for the Python language (for all actively supported | |||
versions of the language: 2.7, >=3.5), including features such as linting, | |||
IntelliSense, code navigation, code formatting, refactoring, snippets, and more! | |||
[关于 VS Code 中 python 的智障补全的解决方案,以 pytorch 为例](https://zhuanlan.zhihu.com/p/69317932) | |||
## Install | |||
In your vim/neovim, run command: | |||
``` | |||
:CocInstall coc-python | |||
``` | |||
## Get start | |||
- To use Microsoft Python Language Server, add `"python.jediEnabled": false` in your settings file, coc-python would download latest stable MPLS for you. | |||
- Checkout `:h coc-status` to have statusline integration with coc.nvim, so you can get download status. | |||
- When jedi is not enabled, the language server will be downloaded. Make sure you have coc statusline integrated, check out `:h coc-status`. | |||
- [Install a linter](https://code.visualstudio.com/docs/python/linting) to get errors and warnings -- you can further customize linting rules to fit your needs. | |||
- Select your preferred Python interpreter/version/environment using the `python.setInterpreter` command. | |||
- **Note** the autoselected python interpreter could be wrong, make sure select the right one for you. | |||
- Install `ctags` for Workspace Symbols, from [here](http://ctags.sourceforge.net/), or using `brew install ctags` on macOS. | |||
## Useful commands | |||
Open the Command List (use `:CocCommand` command in vim) and type in one of the following commands: | |||
| Command | Description | | |||
| ------------------------------------ | ---------------------------------------------------------------------------------------- | | |||
| `python.setInterpreter` | Switch between Python interpreters, versions, and environments. | | |||
| `python.startREPL` | Start an interactive Python REPL using the selected interpreter in the VS Code terminal. | | |||
| `python.execInTerminal` | Runs the active Python file in the VS Code terminal. | | |||
| `python.setLinter` | Switch from PyLint to flake8 or other supported linters. | | |||
| `python.upgradePythonLanguageServer` | Upgrade MPLS to latest daily version. | | |||
To see all available Python commands, open the Command Palette and type `Python`. | |||
**Note:** to enable multiple linters, edit the configuration file instead of use `python.setLinter` command. | |||
## Features | |||
Except from `test`, `debug` and `datascience` features of [vscode-python](https://github.com/Microsoft/vscode-python). | |||
- IDE-like features | |||
- Automatic indenting | |||
- Code navigation ("Go to", "Find all" references) | |||
- Code definition (Peek and hover definition, View signatures) | |||
- Rename refactoring | |||
- Sorting import statements (use the `Python: Sort Imports` command) | |||
- Intellisense and autocomplete (including PEP 484 and PEP 526 support) | |||
- Ability to include custom module paths (e.g. include paths for libraries like Google App Engine, etc.; use the setting `python.autoComplete.extraPaths = []`) | |||
- Code formatting | |||
- Auto formatting of code upon saving changes (default to 'Off') | |||
- Use either [yapf](https://pypi.org/project/yapf/), [autopep8](https://pypi.org/project/autopep8/), or [Black](https://pypi.org/project/black/) for code formatting (defaults to autopep8) | |||
- Linting | |||
- Support for multiple linters with custom settings (default is [Pylint](https://pypi.org/project/pylint/), but [Prospector](https://pypi.org/project/prospector/), [Flake8](https://pypi.org/project/flake8/), [pylama](https://pypi.org/project/pylama/), [pydocstyle](https://pypi.org/project/pydocstyle/), and [mypy](https://pypi.org/project/mypy/) are also supported) | |||
- Snippets | |||
- Miscellaneous | |||
- Running a file or selected text in python terminal | |||
- Automatic activation of environments in the terminal | |||
- Refactoring | |||
- Rename refactorings | |||
- Extract variable refactorings | |||
- Extract method refactorings | |||
- Sort imports | |||
## Options | |||
- `python.autoComplete.addBrackets`:Automatically add brackets for functions, not work for MPLS., default: `false` | |||
- `python.autoComplete.extraPaths`:List of paths to libraries and the like that need to be imported by auto complete engine. E.g. when using Google App SDK, the paths are not in system path, hence need to be added into this list., default: `[]` | |||
- `python.autoComplete.showAdvancedMembers`:Controls appearance of methods with double underscores in the completion list., default: `true` | |||
- `python.autoComplete.typeshedPaths`:Specifies paths to local typeshed repository clone(s) for the Python language server., default: `[]` | |||
- `python.autoUpdateLanguageServer`:Automatically update the language server., default: `true` | |||
- `python.disableInstallationCheck`:Whether to check if Python is installed (also warn when using the macOS-installed Python)., default: `false` | |||
- `python.envFile`:Absolute path to a file containing environment variable definitions., default: `"${workspaceFolder}/.env"` | |||
- `python.trace.server`:Trace level of tsserver, default: `"off"` | |||
- `python.formatting.autopep8Args`:Arguments passed in. Each argument is a separate item in the array., default: `[]` | |||
- `python.formatting.autopep8Path`:Path to autopep8, you can use a custom version of autopep8 by modifying this setting to include the full path., default: `"autopep8"` | |||
- `python.formatting.provider`:Provider for formatting. Possible options include 'autopep8', 'black', and 'yapf'., default: `"autopep8"` | |||
- `python.formatting.blackArgs`:Arguments passed in. Each argument is a separate item in the array., default: `[]` | |||
- `python.formatting.blackPath`:Path to Black, you can use a custom version of Black by modifying this setting to include the full path., default: `"black"` | |||
- `python.formatting.yapfArgs`:Arguments passed in. Each argument is a separate item in the array., default: `[]` | |||
- `python.formatting.yapfPath`:Path to yapf, you can use a custom version of yapf by modifying this setting to include the full path., default: `"yapf"` | |||
- `python.globalModuleInstallation`:Whether to install Python modules globally when not using an environment., default: `false` | |||
- `python.jediEnabled`:Enables Jedi as IntelliSense engine instead of Microsoft Python Analysis Engine., default: `true` | |||
- `python.jediMemoryLimit`:Memory limit for the Jedi completion engine in megabytes. Zero (default) means 1024 MB. -1 means unlimited (disable memory limit check), default: `0` | |||
- `python.jediPath`:Path to directory containing the Jedi library (this path will contain the 'Jedi' sub directory)., default: `""` | |||
- `python.analysis.diagnosticEnabled`: Enable diagnostic support of language server, default: `true` | |||
- `python.analysis.openFilesOnly`:Only show errors and warnings for open files rather than for the entire workspace., default: `true` | |||
- `python.analysis.diagnosticPublishDelay`:Delay before diagnostic messages are transferred to the problems list (in milliseconds)., default: `1000` | |||
- `python.analysis.typeshedPaths`:Paths to look for typeshed modules., default: `[]` | |||
- `python.analysis.errors`:List of diagnostics messages to be shown as errors., default: `[]` | |||
- `python.analysis.warnings`:List of diagnostics messages to be shown as warnings., default: `[]` | |||
- `python.analysis.information`:List of diagnostics messages to be shown as information., default: `[]` | |||
- `python.analysis.disabled`:List of suppressed diagnostic messages., default: `[]` | |||
- `python.analysis.logLevel`:Defines type of log messages language server writes into the output window., default: `"Error"` | |||
- `python.analysis.symbolsHierarchyDepthLimit`:Limits depth of the symbol tree in the document outline., default: `10` | |||
- `python.linting.enabled`:Whether to lint Python files., default: `true` | |||
- `python.linting.flake8Args`:Arguments passed in. Each argument is a separate item in the array., default: `[]` | |||
- `python.linting.flake8CategorySeverity.E`:Severity of Flake8 message type 'E'., default: `"Error"` | |||
- `python.linting.flake8CategorySeverity.F`:Severity of Flake8 message type 'F'., default: `"Error"` | |||
- `python.linting.flake8CategorySeverity.W`:Severity of Flake8 message type 'W'., default: `"Warning"` | |||
- `python.linting.flake8Enabled`:Whether to lint Python files using flake8, default: `false` | |||
- `python.linting.flake8Path`:Path to flake8, you can use a custom version of flake8 by modifying this setting to include the full path., default: `"flake8"` | |||
- `python.linting.ignorePatterns`:Patterns used to exclude files or folders from being linted., default: `[".vscode/*.py","**/site-packages/**/*.py"]` | |||
- `python.linting.lintOnSave`:Whether to lint Python files when saved., default: `true` | |||
- `python.linting.maxNumberOfProblems`:Controls the maximum number of problems produced by the server., default: `100` | |||
- `python.linting.banditArgs`:Arguments passed in. Each argument is a separate item in the array., default: `[]` | |||
- `python.linting.banditEnabled`:Whether to lint Python files using bandit., default: `false` | |||
- `python.linting.banditPath`:Path to bandit, you can use a custom version of bandit by modifying this setting to include the full path., default: `"bandit"` | |||
- `python.linting.mypyArgs`:Arguments passed in. Each argument is a separate item in the array., default: `["--ignore-missing-imports","--follow-imports=silent","--show-column-numbers"]` | |||
- `python.linting.mypyCategorySeverity.error`:Severity of Mypy message type 'Error'., default: `"Error"` | |||
- `python.linting.mypyCategorySeverity.note`:Severity of Mypy message type 'Note'., default: `"Information"` | |||
- `python.linting.mypyEnabled`:Whether to lint Python files using mypy., default: `false` | |||
- `python.linting.mypyPath`:Path to mypy, you can use a custom version of mypy by modifying this setting to include the full path., default: `"mypy"` | |||
- `python.linting.pep8Args`:Arguments passed in. Each argument is a separate item in the array., default: `[]` | |||
- `python.linting.pep8CategorySeverity.E`:Severity of Pep8 message type 'E'., default: `"Error"` | |||
- `python.linting.pep8CategorySeverity.W`:Severity of Pep8 message type 'W'., default: `"Warning"` | |||
- `python.linting.pep8Enabled`:Whether to lint Python files using pep8, default: `false` | |||
- `python.linting.pep8Path`:Path to pep8, you can use a custom version of pep8 by modifying this setting to include the full path., default: `"pep8"` | |||
- `python.linting.prospectorArgs`:Arguments passed in. Each argument is a separate item in the array., default: `[]` | |||
- `python.linting.prospectorEnabled`:Whether to lint Python files using prospector., default: `false` | |||
- `python.linting.prospectorPath`:Path to Prospector, you can use a custom version of prospector by modifying this setting to include the full path., default: `"prospector"` | |||
- `python.linting.pydocstyleArgs`:Arguments passed in. Each argument is a separate item in the array., default: `[]` | |||
- `python.linting.pydocstyleEnabled`:Whether to lint Python files using pydocstyle, default: `false` | |||
- `python.linting.pydocstylePath`:Path to pydocstyle, you can use a custom version of pydocstyle by modifying this setting to include the full path., default: `"pydocstyle"` | |||
- `python.linting.pylamaArgs`:Arguments passed in. Each argument is a separate item in the array., default: `[]` | |||
- `python.linting.pylamaEnabled`:Whether to lint Python files using pylama., default: `false` | |||
- `python.linting.pylamaPath`:Path to pylama, you can use a custom version of pylama by modifying this setting to include the full path., default: `"pylama"` | |||
- `python.linting.pylintArgs`:Arguments passed in. Each argument is a separate item in the array., default: `[]` | |||
- `python.linting.pylintCategorySeverity.convention`:Severity of Pylint message type 'Convention/C'., default: `"Information"` | |||
- `python.linting.pylintCategorySeverity.error`:Severity of Pylint message type 'Error/E'., default: `"Error"` | |||
- `python.linting.pylintCategorySeverity.fatal`:Severity of Pylint message type 'Fatal/F'., default: `"Error"` | |||
- `python.linting.pylintCategorySeverity.refactor`:Severity of Pylint message type 'Refactor/R'., default: `"Hint"` | |||
- `python.linting.pylintCategorySeverity.warning`:Severity of Pylint message type 'Warning/W'., default: `"Warning"` | |||
- `python.linting.pylintEnabled`:Whether to lint Python files using pylint., default: `true` | |||
- `python.linting.pylintPath`:Path to Pylint, you can use a custom version of pylint by modifying this setting to include the full path., default: `"pylint"` | |||
- `python.linting.pylintUseMinimalCheckers`:Whether to run Pylint with minimal set of rules., default: `true` | |||
- `python.pythonPath`:Path to Python, you can use a custom version of Python by modifying this setting to include the full path., default: `"python"` | |||
- `python.condaPath`:Path to the conda executable to use for activation (version 4.4+)., default: `""` | |||
- `python.pipenvPath`:Path to the pipenv executable to use for activation., default: `"pipenv"` | |||
- `python.poetryPath`:Path to the poetry executable., default: `"poetry"` | |||
- `python.sortImports.args`:Arguments passed in. Each argument is a separate item in the array., default: `[]` | |||
- `python.sortImports.path`:Path to isort script, default using inner version, default: `""` | |||
- `python.terminal.activateEnvironment`:Activate Python Environment in Terminal created using the Extension., default: `true` | |||
- `python.terminal.executeInFileDir`:When executing a file in the terminal, whether to use execute in the file's directory, instead of the current open folder., default: `false` | |||
- `python.terminal.launchArgs`:Python launch arguments to use when executing a file in the terminal., default: `[]` | |||
- `python.venvFolders`:Folders in your home directory to look into for virtual environments., default: `["envs",".pyenv",".direnv"]` | |||
- `python.venvPath`:Path to folder with a list of Virtual Environments (e.g. ~/.pyenv, ~/Envs, ~/.virtualenvs)., default: `""` | |||
- `python.workspaceSymbols.ctagsPath`:Fully qualified path to the ctags executable (else leave as ctags, assuming it is in current path)., default: `"ctags"` | |||
- `python.workspaceSymbols.enabled`:Set to 'false' to disable Workspace Symbol provider using ctags., default: `true` | |||
- `python.workspaceSymbols.exclusionPatterns`:Pattern used to exclude files and folders from ctags See http://ctags.sourceforge.net/ctags.html., default: `["**/site-packages/**"]` | |||
- `python.workspaceSymbols.rebuildOnFileSave`:Whether to re-build the tags file on when changes made to python files are saved., default: `true` | |||
- `python.workspaceSymbols.rebuildOnStart`:Whether to re-build the tags file on start (defaults to true)., default: `true` | |||
- `python.workspaceSymbols.tagFilePath`:Fully qualified path to tag file (exuberant ctags file), used to provide workspace symbols., default: `"${workspaceFolder}/.vscode/tags"` | |||
## F.A.Q | |||
**Q:** `"python.linting.enabled": false` not work when jedi disabled. | |||
**A:** That setting only works when python files are used, not MPLS, you have to use `"python.analysis.diagnosticEnabled": false` to disable diagnostics from language server. | |||
**Q:** MPLS using too much memory and CPU. | |||
**A:** It's bug of MPLS, checkout https://github.com/Microsoft/python-language-server/issues/832. | |||
**Q:** MPLS doesn't work with unsaved buffer. | |||
**A:** Yes, it's not. You have to save your buffer to make it work. | |||
**Q:** Get unable to find document error from MPLS. | |||
**A:** Some filename would cause MPLS throw this error. | |||
## License | |||
MIT |
@@ -1,915 +0,0 @@ | |||
{ | |||
"name": "coc-python", | |||
"version": "1.2.12", | |||
"languageServerVersion": "0.2.92", | |||
"description": "Python extension for coc.nvim, forked from vscode-python.", | |||
"main": "lib/index.js", | |||
"publisher": "chemzqm", | |||
"repository": { | |||
"type": "git", | |||
"url": "https://github.com/neoclide/coc-python" | |||
}, | |||
"bugs": { | |||
"url": "https://github.com/neoclide/coc-python/issues" | |||
}, | |||
"keywords": [ | |||
"python", | |||
"coc.nvim", | |||
"mpls" | |||
], | |||
"engines": { | |||
"coc": ">= 0.0.73" | |||
}, | |||
"scripts": { | |||
"clean": "rimraf lib", | |||
"build": "webpack", | |||
"prepare": "npx npm-run-all clean build" | |||
}, | |||
"activationEvents": [ | |||
"onLanguage:python", | |||
"onLanguage:jupyter", | |||
"onCommand:python.sortImports", | |||
"onCommand:python.viewOutput", | |||
"onCommand:python.startREPL", | |||
"onCommand:python.goToPythonObject", | |||
"onCommand:python.setLinter", | |||
"onCommand:python.enableLinting", | |||
"onCommand:python.enableSourceMapSupport" | |||
], | |||
"contributes": { | |||
"snippets": [ | |||
{ | |||
"language": "python", | |||
"path": "./snippets/python.json" | |||
} | |||
], | |||
"jsonValidation": [ | |||
{ | |||
"fileMatch": ".condarc", | |||
"url": "./schemas/condarc.json" | |||
}, | |||
{ | |||
"fileMatch": "environment.yml", | |||
"url": "./schemas/conda-environment.json" | |||
}, | |||
{ | |||
"fileMatch": "meta.yaml", | |||
"url": "./schemas/conda-meta.json" | |||
} | |||
], | |||
"yamlValidation": [ | |||
{ | |||
"fileMatch": ".condarc", | |||
"url": "./schemas/condarc.json" | |||
}, | |||
{ | |||
"fileMatch": "environment.yml", | |||
"url": "./schemas/conda-environment.json" | |||
}, | |||
{ | |||
"fileMatch": "meta.yaml", | |||
"url": "./schemas/conda-meta.json" | |||
} | |||
], | |||
"commands": [ | |||
{ | |||
"command": "python.enableSourceMapSupport", | |||
"title": "Enable source map support for extension debugging", | |||
"category": "Python" | |||
}, | |||
{ | |||
"command": "python.sortImports", | |||
"title": "Sort Imports", | |||
"category": "Python Refactor" | |||
}, | |||
{ | |||
"command": "python.startREPL", | |||
"title": "Start REPL", | |||
"category": "Python" | |||
}, | |||
{ | |||
"command": "python.createTerminal", | |||
"title": "Create Terminal", | |||
"category": "Python" | |||
}, | |||
{ | |||
"command": "python.buildWorkspaceSymbols", | |||
"title": "Build Workspace Symbols", | |||
"category": "Python" | |||
}, | |||
{ | |||
"command": "python.execInTerminal", | |||
"title": "Run Python File in Terminal", | |||
"category": "Python" | |||
}, | |||
{ | |||
"command": "python.setInterpreter", | |||
"title": "Select Interpreter", | |||
"category": "Python" | |||
}, | |||
{ | |||
"command": "python.upgradePythonLanguageServer", | |||
"title": "Upgrade MPLS to latest stable version, restart coc.nvim required to take effect.", | |||
"category": "Python" | |||
}, | |||
{ | |||
"command": "python.viewOutput", | |||
"title": "Show output", | |||
"category": "Python" | |||
}, | |||
{ | |||
"command": "python.goToPythonObject", | |||
"title": "Go to Python Object", | |||
"category": "Python" | |||
}, | |||
{ | |||
"command": "python.setLinter", | |||
"title": "Select Linter", | |||
"category": "Python" | |||
}, | |||
{ | |||
"command": "python.enableLinting", | |||
"title": "Enable Linting", | |||
"category": "Python" | |||
}, | |||
{ | |||
"command": "python.runLinting", | |||
"title": "Run Linting", | |||
"category": "Python" | |||
} | |||
], | |||
"configuration": { | |||
"type": "object", | |||
"title": "Python", | |||
"properties": { | |||
"python.autoComplete.addBrackets": { | |||
"type": "boolean", | |||
"default": false, | |||
"description": "Automatically add brackets for functions.", | |||
"scope": "resource" | |||
}, | |||
"python.autoComplete.extraPaths": { | |||
"type": "array", | |||
"default": [], | |||
"description": "List of paths to libraries and the like that need to be imported by auto complete engine. E.g. when using Google App SDK, the paths are not in system path, hence need to be added into this list.", | |||
"scope": "resource" | |||
}, | |||
"python.autoComplete.showAdvancedMembers": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Controls appearance of methods with double underscores in the completion list.", | |||
"scope": "resource" | |||
}, | |||
"python.jediShortcut": { | |||
"type": "string", | |||
"description": "Shortcut used for jedi completion.", | |||
"default": "JD", | |||
"scope": "resource" | |||
}, | |||
"python.autoComplete.typeshedPaths": { | |||
"type": "array", | |||
"items": { | |||
"type": "string" | |||
}, | |||
"default": [], | |||
"description": "Specifies paths to local typeshed repository clone(s) for the Python language server.", | |||
"scope": "resource" | |||
}, | |||
"python.autoUpdateLanguageServer": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Automatically update the language server.", | |||
"scope": "application" | |||
}, | |||
"python.disableInstallationCheck": { | |||
"type": "boolean", | |||
"default": false, | |||
"description": "Whether to check if Python is installed (also warn when using the macOS-installed Python).", | |||
"scope": "resource" | |||
}, | |||
"python.envFile": { | |||
"type": "string", | |||
"description": "Absolute path to a file containing environment variable definitions.", | |||
"default": "${workspaceFolder}/.env", | |||
"scope": "resource" | |||
}, | |||
"python.trace.server": { | |||
"type": "string", | |||
"default": "off", | |||
"enum": [ | |||
"off", | |||
"messages", | |||
"verbose" | |||
], | |||
"description": "Trace level of tsserver" | |||
}, | |||
"python.formatting.autopep8Args": { | |||
"type": "array", | |||
"description": "Arguments passed in. Each argument is a separate item in the array.", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"scope": "resource" | |||
}, | |||
"python.formatting.autopep8Path": { | |||
"type": "string", | |||
"default": "autopep8", | |||
"description": "Path to autopep8, you can use a custom version of autopep8 by modifying this setting to include the full path.", | |||
"scope": "resource" | |||
}, | |||
"python.formatting.provider": { | |||
"type": "string", | |||
"default": "autopep8", | |||
"description": "Provider for formatting. Possible options include 'autopep8', 'black', and 'yapf'.", | |||
"enum": [ | |||
"autopep8", | |||
"black", | |||
"yapf", | |||
"none" | |||
], | |||
"scope": "resource" | |||
}, | |||
"python.formatting.blackArgs": { | |||
"type": "array", | |||
"description": "Arguments passed in. Each argument is a separate item in the array.", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"scope": "resource" | |||
}, | |||
"python.formatting.blackPath": { | |||
"type": "string", | |||
"default": "black", | |||
"description": "Path to Black, you can use a custom version of Black by modifying this setting to include the full path.", | |||
"scope": "resource" | |||
}, | |||
"python.formatting.yapfArgs": { | |||
"type": "array", | |||
"description": "Arguments passed in. Each argument is a separate item in the array.", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"scope": "resource" | |||
}, | |||
"python.formatting.yapfPath": { | |||
"type": "string", | |||
"default": "yapf", | |||
"description": "Path to yapf, you can use a custom version of yapf by modifying this setting to include the full path.", | |||
"scope": "resource" | |||
}, | |||
"python.globalModuleInstallation": { | |||
"type": "boolean", | |||
"default": false, | |||
"description": "Whether to install Python modules globally when not using an environment.", | |||
"scope": "resource" | |||
}, | |||
"python.jediEnabled": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Enables Jedi as IntelliSense engine instead of Microsoft Python Analysis Engine.", | |||
"scope": "resource" | |||
}, | |||
"python.jediMemoryLimit": { | |||
"type": "number", | |||
"default": 0, | |||
"description": "Memory limit for the Jedi completion engine in megabytes. Zero (default) means 1024 MB. -1 means unlimited (disable memory limit check)", | |||
"scope": "resource" | |||
}, | |||
"python.jediPath": { | |||
"type": "string", | |||
"default": "", | |||
"description": "Path to directory containing the Jedi library (this path will contain the 'Jedi' sub directory).", | |||
"scope": "resource" | |||
}, | |||
"python.analysis.downloadChannel": { | |||
"type": "string", | |||
"enum": [ | |||
"stable", | |||
"beta", | |||
"daily" | |||
], | |||
"default": "stable", | |||
"description": "Defines how to down MPLS, use beta for beta version, or daily for upgrade to stable when possible, stable means MPLS only upgraded when coc-python upgraded." | |||
}, | |||
"python.analysis.openFilesOnly": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Only show errors and warnings for open files rather than for the entire workspace.", | |||
"scope": "resource" | |||
}, | |||
"python.analysis.diagnosticEnabled": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Enable diagnostic support of language server.", | |||
"scope": "resource" | |||
}, | |||
"python.analysis.diagnosticPublishDelay": { | |||
"type": "integer", | |||
"default": 1000, | |||
"description": "Delay before diagnostic messages are transferred to the problems list (in milliseconds).", | |||
"scope": "resource" | |||
}, | |||
"python.analysis.typeshedPaths": { | |||
"type": "array", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"description": "Paths to look for typeshed modules.", | |||
"scope": "resource" | |||
}, | |||
"python.analysis.errors": { | |||
"type": "array", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"description": "List of diagnostics messages to be shown as errors.", | |||
"scope": "resource" | |||
}, | |||
"python.analysis.warnings": { | |||
"type": "array", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"description": "List of diagnostics messages to be shown as warnings.", | |||
"scope": "resource" | |||
}, | |||
"python.analysis.information": { | |||
"type": "array", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"description": "List of diagnostics messages to be shown as information.", | |||
"scope": "resource" | |||
}, | |||
"python.analysis.disabled": { | |||
"type": "array", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"description": "List of suppressed diagnostic messages.", | |||
"scope": "resource" | |||
}, | |||
"python.analysis.logLevel": { | |||
"type": "string", | |||
"enum": [ | |||
"Error", | |||
"Warning", | |||
"Information", | |||
"Trace" | |||
], | |||
"default": "Error", | |||
"description": "Defines type of log messages language server writes into the output window.", | |||
"scope": "resource" | |||
}, | |||
"python.analysis.symbolsHierarchyDepthLimit": { | |||
"type": "integer", | |||
"default": 10, | |||
"description": "Limits depth of the symbol tree in the document outline.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.enabled": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Whether to lint Python files.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.flake8Args": { | |||
"type": "array", | |||
"description": "Arguments passed in. Each argument is a separate item in the array.", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"scope": "resource" | |||
}, | |||
"python.linting.flake8CategorySeverity.E": { | |||
"type": "string", | |||
"default": "Error", | |||
"description": "Severity of Flake8 message type 'E'.", | |||
"enum": [ | |||
"Hint", | |||
"Error", | |||
"Information", | |||
"Warning" | |||
], | |||
"scope": "resource" | |||
}, | |||
"python.linting.flake8CategorySeverity.F": { | |||
"type": "string", | |||
"default": "Error", | |||
"description": "Severity of Flake8 message type 'F'.", | |||
"enum": [ | |||
"Hint", | |||
"Error", | |||
"Information", | |||
"Warning" | |||
], | |||
"scope": "resource" | |||
}, | |||
"python.linting.flake8CategorySeverity.W": { | |||
"type": "string", | |||
"default": "Warning", | |||
"description": "Severity of Flake8 message type 'W'.", | |||
"enum": [ | |||
"Hint", | |||
"Error", | |||
"Information", | |||
"Warning" | |||
], | |||
"scope": "resource" | |||
}, | |||
"python.linting.flake8Enabled": { | |||
"type": "boolean", | |||
"default": false, | |||
"description": "Whether to lint Python files using flake8", | |||
"scope": "resource" | |||
}, | |||
"python.linting.flake8Path": { | |||
"type": "string", | |||
"default": "flake8", | |||
"description": "Path to flake8, you can use a custom version of flake8 by modifying this setting to include the full path.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.ignorePatterns": { | |||
"type": "array", | |||
"description": "Patterns used to exclude files or folders from being linted.", | |||
"default": [ | |||
".vscode/*.py", | |||
"**/site-packages/**/*.py" | |||
], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"scope": "resource" | |||
}, | |||
"python.linting.lintOnSave": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Whether to lint Python files when saved.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.maxNumberOfProblems": { | |||
"type": "number", | |||
"default": 100, | |||
"description": "Controls the maximum number of problems produced by the server.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.banditArgs": { | |||
"type": "array", | |||
"description": "Arguments passed in. Each argument is a separate item in the array.", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"scope": "resource" | |||
}, | |||
"python.linting.banditEnabled": { | |||
"type": "boolean", | |||
"default": false, | |||
"description": "Whether to lint Python files using bandit.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.banditPath": { | |||
"type": "string", | |||
"default": "bandit", | |||
"description": "Path to bandit, you can use a custom version of bandit by modifying this setting to include the full path.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.mypyArgs": { | |||
"type": "array", | |||
"description": "Arguments passed in. Each argument is a separate item in the array.", | |||
"default": [ | |||
"--ignore-missing-imports", | |||
"--follow-imports=silent", | |||
"--show-column-numbers" | |||
], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"scope": "resource" | |||
}, | |||
"python.linting.mypyCategorySeverity.error": { | |||
"type": "string", | |||
"default": "Error", | |||
"description": "Severity of Mypy message type 'Error'.", | |||
"enum": [ | |||
"Hint", | |||
"Error", | |||
"Information", | |||
"Warning" | |||
], | |||
"scope": "resource" | |||
}, | |||
"python.linting.mypyCategorySeverity.note": { | |||
"type": "string", | |||
"default": "Information", | |||
"description": "Severity of Mypy message type 'Note'.", | |||
"enum": [ | |||
"Hint", | |||
"Error", | |||
"Information", | |||
"Warning" | |||
], | |||
"scope": "resource" | |||
}, | |||
"python.linting.mypyEnabled": { | |||
"type": "boolean", | |||
"default": false, | |||
"description": "Whether to lint Python files using mypy.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.mypyPath": { | |||
"type": "string", | |||
"default": "mypy", | |||
"description": "Path to mypy, you can use a custom version of mypy by modifying this setting to include the full path.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.pep8Args": { | |||
"type": "array", | |||
"description": "Arguments passed in. Each argument is a separate item in the array.", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"scope": "resource" | |||
}, | |||
"python.linting.pep8CategorySeverity.E": { | |||
"type": "string", | |||
"default": "Error", | |||
"description": "Severity of Pep8 message type 'E'.", | |||
"enum": [ | |||
"Hint", | |||
"Error", | |||
"Information", | |||
"Warning" | |||
], | |||
"scope": "resource" | |||
}, | |||
"python.linting.pep8CategorySeverity.W": { | |||
"type": "string", | |||
"default": "Warning", | |||
"description": "Severity of Pep8 message type 'W'.", | |||
"enum": [ | |||
"Hint", | |||
"Error", | |||
"Information", | |||
"Warning" | |||
], | |||
"scope": "resource" | |||
}, | |||
"python.linting.pep8Enabled": { | |||
"type": "boolean", | |||
"default": false, | |||
"description": "Whether to lint Python files using pep8", | |||
"scope": "resource" | |||
}, | |||
"python.linting.pep8Path": { | |||
"type": "string", | |||
"default": "pep8", | |||
"description": "Path to pep8, you can use a custom version of pep8 by modifying this setting to include the full path.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.prospectorArgs": { | |||
"type": "array", | |||
"description": "Arguments passed in. Each argument is a separate item in the array.", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"scope": "resource" | |||
}, | |||
"python.linting.prospectorEnabled": { | |||
"type": "boolean", | |||
"default": false, | |||
"description": "Whether to lint Python files using prospector.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.prospectorPath": { | |||
"type": "string", | |||
"default": "prospector", | |||
"description": "Path to Prospector, you can use a custom version of prospector by modifying this setting to include the full path.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.pydocstyleArgs": { | |||
"type": "array", | |||
"description": "Arguments passed in. Each argument is a separate item in the array.", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"scope": "resource" | |||
}, | |||
"python.linting.pydocstyleEnabled": { | |||
"type": "boolean", | |||
"default": false, | |||
"description": "Whether to lint Python files using pydocstyle", | |||
"scope": "resource" | |||
}, | |||
"python.linting.pydocstylePath": { | |||
"type": "string", | |||
"default": "pydocstyle", | |||
"description": "Path to pydocstyle, you can use a custom version of pydocstyle by modifying this setting to include the full path.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.pylamaArgs": { | |||
"type": "array", | |||
"description": "Arguments passed in. Each argument is a separate item in the array.", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"scope": "resource" | |||
}, | |||
"python.linting.pylamaEnabled": { | |||
"type": "boolean", | |||
"default": false, | |||
"description": "Whether to lint Python files using pylama.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.pylamaPath": { | |||
"type": "string", | |||
"default": "pylama", | |||
"description": "Path to pylama, you can use a custom version of pylama by modifying this setting to include the full path.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.pylintArgs": { | |||
"type": "array", | |||
"description": "Arguments passed in. Each argument is a separate item in the array.", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"scope": "resource" | |||
}, | |||
"python.linting.pylintCategorySeverity.convention": { | |||
"type": "string", | |||
"default": "Information", | |||
"description": "Severity of Pylint message type 'Convention/C'.", | |||
"enum": [ | |||
"Hint", | |||
"Error", | |||
"Information", | |||
"Warning" | |||
], | |||
"scope": "resource" | |||
}, | |||
"python.linting.pylintCategorySeverity.error": { | |||
"type": "string", | |||
"default": "Error", | |||
"description": "Severity of Pylint message type 'Error/E'.", | |||
"enum": [ | |||
"Hint", | |||
"Error", | |||
"Information", | |||
"Warning" | |||
], | |||
"scope": "resource" | |||
}, | |||
"python.linting.pylintCategorySeverity.fatal": { | |||
"type": "string", | |||
"default": "Error", | |||
"description": "Severity of Pylint message type 'Fatal/F'.", | |||
"enum": [ | |||
"Hint", | |||
"Error", | |||
"Information", | |||
"Warning" | |||
], | |||
"scope": "resource" | |||
}, | |||
"python.linting.pylintCategorySeverity.refactor": { | |||
"type": "string", | |||
"default": "Hint", | |||
"description": "Severity of Pylint message type 'Refactor/R'.", | |||
"enum": [ | |||
"Hint", | |||
"Error", | |||
"Information", | |||
"Warning" | |||
], | |||
"scope": "resource" | |||
}, | |||
"python.linting.pylintCategorySeverity.warning": { | |||
"type": "string", | |||
"default": "Warning", | |||
"description": "Severity of Pylint message type 'Warning/W'.", | |||
"enum": [ | |||
"Hint", | |||
"Error", | |||
"Information", | |||
"Warning" | |||
], | |||
"scope": "resource" | |||
}, | |||
"python.linting.pylintEnabled": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Whether to lint Python files using pylint.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.pylintPath": { | |||
"type": "string", | |||
"default": "pylint", | |||
"description": "Path to Pylint, you can use a custom version of pylint by modifying this setting to include the full path.", | |||
"scope": "resource" | |||
}, | |||
"python.linting.pylintUseMinimalCheckers": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Whether to run Pylint with minimal set of rules.", | |||
"scope": "resource" | |||
}, | |||
"python.pythonPath": { | |||
"type": "string", | |||
"default": "python", | |||
"description": "Path to Python, you can use a custom version of Python by modifying this setting to include the full path.", | |||
"scope": "resource" | |||
}, | |||
"python.condaPath": { | |||
"type": "string", | |||
"default": "", | |||
"description": "Path to the conda executable to use for activation (version 4.4+).", | |||
"scope": "resource" | |||
}, | |||
"python.pipenvPath": { | |||
"type": "string", | |||
"default": "pipenv", | |||
"description": "Path to the pipenv executable to use for activation.", | |||
"scope": "resource" | |||
}, | |||
"python.poetryPath": { | |||
"type": "string", | |||
"default": "poetry", | |||
"description": "Path to the poetry executable.", | |||
"scope": "resource" | |||
}, | |||
"python.sortImports.args": { | |||
"type": "array", | |||
"description": "Arguments passed in. Each argument is a separate item in the array.", | |||
"default": [], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"scope": "resource" | |||
}, | |||
"python.sortImports.path": { | |||
"type": "string", | |||
"description": "Path to isort script, default using inner version", | |||
"default": "", | |||
"scope": "resource" | |||
}, | |||
"python.terminal.activateEnvironment": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Activate Python Environment in Terminal created using the Extension.", | |||
"scope": "resource" | |||
}, | |||
"python.terminal.executeInFileDir": { | |||
"type": "boolean", | |||
"default": false, | |||
"description": "When executing a file in the terminal, whether to use execute in the file's directory, instead of the current open folder.", | |||
"scope": "resource" | |||
}, | |||
"python.terminal.launchArgs": { | |||
"type": "array", | |||
"default": [], | |||
"description": "Python launch arguments to use when executing a file in the terminal.", | |||
"scope": "resource" | |||
}, | |||
"python.venvFolders": { | |||
"type": "array", | |||
"default": [ | |||
"envs", | |||
".pyenv", | |||
".direnv" | |||
], | |||
"description": "Folders in your home directory to look into for virtual environments.", | |||
"scope": "resource", | |||
"items": { | |||
"type": "string" | |||
} | |||
}, | |||
"python.venvPath": { | |||
"type": "string", | |||
"default": "", | |||
"description": "Path to folder with a list of Virtual Environments (e.g. ~/.pyenv, ~/Envs, ~/.virtualenvs).", | |||
"scope": "resource" | |||
}, | |||
"python.workspaceSymbols.ctagsPath": { | |||
"type": "string", | |||
"default": "ctags", | |||
"description": "Fully qualified path to the ctags executable (else leave as ctags, assuming it is in current path).", | |||
"scope": "resource" | |||
}, | |||
"python.workspaceSymbols.enabled": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Set to 'false' to disable Workspace Symbol provider using ctags.", | |||
"scope": "resource" | |||
}, | |||
"python.workspaceSymbols.exclusionPatterns": { | |||
"type": "array", | |||
"default": [ | |||
"**/site-packages/**" | |||
], | |||
"items": { | |||
"type": "string" | |||
}, | |||
"description": "Pattern used to exclude files and folders from ctags See http://ctags.sourceforge.net/ctags.html.", | |||
"scope": "resource" | |||
}, | |||
"python.workspaceSymbols.rebuildOnFileSave": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Whether to re-build the tags file on when changes made to python files are saved.", | |||
"scope": "resource" | |||
}, | |||
"python.workspaceSymbols.rebuildOnStart": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Whether to re-build the tags file on start (defaults to true).", | |||
"scope": "resource" | |||
}, | |||
"python.workspaceSymbols.tagFilePath": { | |||
"type": "string", | |||
"default": "${workspaceFolder}/.vscode/tags", | |||
"description": "Fully qualified path to tag file (exuberant ctag file), used to provide workspace symbols.", | |||
"scope": "resource" | |||
} | |||
} | |||
} | |||
}, | |||
"author": "chemzqm@gmail.com", | |||
"license": "MIT", | |||
"devDependencies": { | |||
"@chemzqm/tsconfig": "^0.0.3", | |||
"@chemzqm/tslint-config": "^1.0.18", | |||
"@types/del": "^4.0.0", | |||
"@types/diff-match-patch": "^1.0.32", | |||
"@types/event-stream": "^3.3.34", | |||
"@types/fs-extra": "^5.0.5", | |||
"@types/glob": "^7.1.1", | |||
"@types/loader-utils": "^1.1.3", | |||
"@types/lodash": "^4.14.123", | |||
"@types/md5": "^2.1.33", | |||
"@types/mocha": "^5.2.6", | |||
"@types/node": "^11.13.9", | |||
"@types/promisify-node": "^0.4.0", | |||
"@types/request": "^2.48.1", | |||
"@types/sinon": "^7.0.11", | |||
"@types/temp": "^0.8.34", | |||
"@types/tmp": "0.1.0", | |||
"@types/untildify": "^3.0.0", | |||
"@types/uuid": "^3.4.4", | |||
"@types/which": "^1.3.1", | |||
"@types/winreg": "^1.2.30", | |||
"arch": "^2.1.1", | |||
"azure-storage": "^2.10.3", | |||
"coc.nvim": "^0.0.73", | |||
"diff-match-patch": "^1.0.4", | |||
"fs-extra": "^7.0.1", | |||
"fuzzy": "^0.1.3", | |||
"glob": "^7.1.3", | |||
"iconv-lite": "^0.4.24", | |||
"inversify": "^5.0.1", | |||
"line-by-line": "^0.1.6", | |||
"lodash": "^4.17.11", | |||
"md5": "^2.2.1", | |||
"minimatch": "^3.0.4", | |||
"named-js-regexp": "^1.3.5", | |||
"node-stream-zip": "^1.8.0", | |||
"pidusage": "^1.2.0", | |||
"reflect-metadata": "^0.1.13", | |||
"request": "^2.88.0", | |||
"request-progress": "^3.0.0", | |||
"rimraf": "^2.6.3", | |||
"rxjs": "^5.5.9", | |||
"semver": "^5.5.0", | |||
"sudo-prompt": "^8.2.5", | |||
"tmp": "^0.1.0", | |||
"tree-kill": "^1.2.1", | |||
"ts-loader": "^6.0.3", | |||
"tslint": "^5.16.0", | |||
"typescript": "^3.4.0", | |||
"typescript-char": "^0.0.0", | |||
"uint64be": "^2.0.2", | |||
"unicode": "^11.0.1", | |||
"untildify": "^4.0.0", | |||
"vscode-languageserver": "^5.3.0-next.5", | |||
"vscode-languageserver-protocol": "^3.15.0-next.4", | |||
"webpack": "^4.35.0", | |||
"webpack-cli": "^3.3.4", | |||
"which": "^1.3.1", | |||
"winreg": "^1.2.4", | |||
"xml2js": "^0.4.19" | |||
}, | |||
"dependencies": {} | |||
} |
@@ -1,223 +0,0 @@ | |||
{ | |||
"python.command.python.sortImports.title": "Sort Imports", | |||
"python.command.python.startREPL.title": "Start REPL", | |||
"python.command.python.createTerminal.title": "Create Terminal", | |||
"python.command.python.buildWorkspaceSymbols.title": "Build Workspace Symbols", | |||
"python.command.python.runtests.title": "Run All Unit Tests", | |||
"python.command.python.debugtests.title": "Debug All Unit Tests", | |||
"python.command.python.execInTerminal.title": "Run Python File in Terminal", | |||
"python.command.python.setInterpreter.title": "Select Interpreter", | |||
"python.command.python.updateSparkLibrary.title": "Update Workspace PySpark Libraries", | |||
"python.command.python.refactorExtractVariable.title": "Extract Variable", | |||
"python.command.python.refactorExtractMethod.title": "Extract Method", | |||
"python.command.python.viewOutput.title": "Show Output", | |||
"python.command.python.viewTestOutput.title": "Show Unit Test Output", | |||
"python.command.python.selectAndRunTestMethod.title": "Run Unit Test Method ...", | |||
"python.command.python.selectAndDebugTestMethod.title": "Debug Unit Test Method ...", | |||
"python.command.python.selectAndRunTestFile.title": "Run Unit Test File ...", | |||
"python.command.python.runCurrentTestFile.title": "Run Current Unit Test File", | |||
"python.command.python.runFailedTests.title": "Run Failed Unit Tests", | |||
"python.command.python.discoverTests.title": "Discover Unit Tests", | |||
"python.command.python.stopUnitTests.title": "Stop", | |||
"python.command.python.configureTests.title": "Configure Unit Tests", | |||
"python.command.python.execSelectionInTerminal.title": "Run Selection/Line in Python Terminal", | |||
"python.command.python.execSelectionInDjangoShell.title": "Run Selection/Line in Django Shell", | |||
"python.command.python.goToPythonObject.title": "Go to Python Object", | |||
"python.command.python.setLinter.title": "Select Linter", | |||
"python.command.python.enableLinting.title": "Enable Linting", | |||
"python.command.python.runLinting.title": "Run Linting", | |||
"python.command.python.datascience.runFileInteractive.title": "Run Current File in Python Interactive Window", | |||
"python.command.python.datascience.runallcells.title": "Run All Cells", | |||
"python.command.python.datascience.runallcellsabove.title": "Run Above", | |||
"python.command.python.datascience.runcellandallbelow.title": "Run Below", | |||
"python.command.python.datascience.runtoline.title": "Run To Line in Python Interactive window", | |||
"python.command.python.datascience.runfromline.title": "Run From Line in Python Interactive window", | |||
"python.command.python.datascience.runcurrentcell.title": "Run Current Cell", | |||
"python.command.python.datascience.runcurrentcelladvance.title": "Run Current Cell And Advance", | |||
"python.command.python.datascience.execSelectionInteractive.title": "Run Selection/Line in Python Interactive window", | |||
"python.command.python.datascience.runcell.title": "Run Cell", | |||
"python.command.python.datascience.showhistorypane.title": "Show Python Interactive window", | |||
"python.command.python.datascience.selectjupyteruri.title": "Specify Jupyter server URI", | |||
"python.command.python.datascience.importnotebook.title": "Import Jupyter Notebook", | |||
"python.command.python.datascience.importnotebookonfile.title": "Import Jupyter Notebook", | |||
"python.command.python.enableSourceMapSupport.title": "Enable source map support for extension debugging", | |||
"python.command.python.datascience.exportoutputasnotebook.title": "Export Python Interactive window as Jupyter Notebook", | |||
"python.command.python.datascience.exportfileasnotebook.title": "Export Current Python file as Jupyter Notebook", | |||
"python.command.python.datascience.exportfileandoutputasnotebook.title": "Export Current Python File and Output as Jupyter Notebook", | |||
"python.command.python.datascience.undocells.title": "Undo last Python Interactive action", | |||
"python.command.python.datascience.redocells.title": "Redo last Python Interactive action", | |||
"python.command.python.datascience.removeallcells.title": "Delete all Python Interactive cells", | |||
"python.command.python.datascience.interruptkernel.title": "Interrupt iPython Kernel", | |||
"python.command.python.datascience.restartkernel.title": "Restart iPython Kernel", | |||
"python.command.python.datascience.expandallcells.title": "Expand all Python Interactive cells", | |||
"python.command.python.datascience.collapseallcells.title": "Collapse all Python Interactive cells", | |||
"python.snippet.launch.standard.label": "Python: Current File", | |||
"python.snippet.launch.standard.description": "Debug a Python Program with Standard Output", | |||
"python.snippet.launch.pyspark.label": "Python: PySpark", | |||
"python.snippet.launch.pyspark.description": "Debug PySpark", | |||
"python.snippet.launch.module.label": "Python: Module", | |||
"python.snippet.launch.module.description": "Debug a Python Module", | |||
"python.snippet.launch.terminal.label": "Python: Terminal (integrated)", | |||
"python.snippet.launch.terminal.description": "Debug a Python program with Integrated Terminal/Console", | |||
"python.snippet.launch.externalTerminal.label": "Python: Terminal (external)", | |||
"python.snippet.launch.externalTerminal.description": "Debug a Python program with External Terminal/Console", | |||
"python.snippet.launch.django.label": "Python: Django", | |||
"python.snippet.launch.django.description": "Debug a Django Application", | |||
"python.snippet.launch.flask.label": "Python: Flask", | |||
"python.snippet.launch.flask.description": "Debug a Flask Application", | |||
"python.snippet.launch.flaskOld.label": "Python: Flask (0.10.x or earlier)", | |||
"python.snippet.launch.flaskOld.description": "Debug an older styled Flask Application", | |||
"python.snippet.launch.gevent.label": "Python: Gevent", | |||
"python.snippet.launch.gevent.description": "Debug a Gevent Application", | |||
"python.snippet.launch.pyramid.label": "Python: Pyramid Application", | |||
"python.snippet.launch.pyramid.description": "Debug a Pyramid Application", | |||
"python.snippet.launch.watson.label": "Python: Watson Application", | |||
"python.snippet.launch.watson.description": "Debug a Watson Application", | |||
"python.snippet.launch.attach.label": "Python: Attach", | |||
"python.snippet.launch.attach.description": "Attach the Debugger for Remote Debugging", | |||
"python.snippet.launch.scrapy.label": "Python: Scrapy", | |||
"python.snippet.launch.scrapy.description": "Scrapy with Integrated Terminal/Console", | |||
"LanguageService.bannerMessage": "Can you please take 2 minutes to tell us how the Python Language Server is working for you?", | |||
"LanguageService.bannerLabelYes": "Yes, take survey now", | |||
"LanguageService.bannerLabelNo": "No, thanks", | |||
"LanguageService.lsFailedToStart": "We encountered an issue starting the Language Server. Reverting to the alternative, Jedi. Check the Python output panel for details.", | |||
"LanguageService.lsFailedToDownload": "We encountered an issue downloading the Language Server. Reverting to the alternative, Jedi. Check the Python output panel for details.", | |||
"LanguageService.lsFailedToExtract": "We encountered an issue extracting the Language Server. Reverting to the alternative, Jedi. Check the Python output panel for details.", | |||
"DataScience.unknownMimeTypeFormat": "Mime type {0} is not currently supported.", | |||
"DataScience.historyTitle": "Python Interactive", | |||
"DataScience.dataExplorerTitle": "Data Viewer", | |||
"DataScience.badWebPanelFormatString": "<html><body><h1>{0} is not a valid file name</h1></body></html>", | |||
"DataScience.sessionDisposed": "Cannot execute code, session has been disposed.", | |||
"DataScience.exportDialogTitle": "Export to Jupyter Notebook", | |||
"DataScience.exportDialogFilter": "Jupyter Notebooks", | |||
"DataScience.exportDialogComplete": "Notebook written to {0}", | |||
"DataScience.exportDialogFailed": "Failed to export notebook. {0}", | |||
"DataScience.exportOpenQuestion": "Open in browser", | |||
"DataScience.collapseInputTooltip": "Collapse input block", | |||
"DataScience.importDialogTitle": "Import Jupyter Notebook", | |||
"DataScience.importDialogFilter": "Jupyter Notebooks", | |||
"DataScience.notebookCheckForImportYes": "Import", | |||
"DataScience.notebookCheckForImportNo": "Later", | |||
"DataScience.notebookCheckForImportDontAskAgain": "Don't Ask Again", | |||
"DataScience.notebookCheckForImportTitle": "Do you want to import the Jupyter Notebook into Python code?", | |||
"DataScience.jupyterNotSupported": "Running cells requires Jupyter notebooks to be installed.", | |||
"DataScience.jupyterNotSupportedBecauseOfEnvironment": "Activating {0} to run Jupyter failed with {1}.", | |||
"DataScience.jupyterNbConvertNotSupported": "Importing notebooks requires Jupyter nbconvert to be installed.", | |||
"DataScience.jupyterLaunchNoURL": "Failed to find the URL of the launched Jupyter notebook server", | |||
"DataScience.jupyterLaunchTimedOut": "The Jupyter notebook server failed to launch in time", | |||
"DataScience.jupyterServerCrashed": "Jupyter server crashed. Unable to connect. \r\nError code from jupyter: {0}", | |||
"DataScience.pythonInteractiveHelpLink": "Get more help", | |||
"DataScience.importingFormat": "Importing {0}", | |||
"DataScience.startingJupyter": "Starting Jupyter server", | |||
"DataScience.connectingToJupyter": "Connecting to Jupyter server", | |||
"Interpreters.RefreshingInterpreters": "Refreshing Python Interpreters", | |||
"Interpreters.LoadingInterpreters": "Loading Python Interpreters", | |||
"DataScience.restartKernelMessage": "Do you want to restart the iPython kernel? All variables will be lost.", | |||
"DataScience.restartKernelMessageYes": "Restart", | |||
"DataScience.restartKernelMessageNo": "Cancel", | |||
"DataScience.restartingKernelFailed": "Kernel restart failed. Jupyter server is hung. Please reload VS Code.", | |||
"DataScience.interruptingKernelFailed": "Kernel interrupt failed. Jupyter server is hung. Please reload VS Code.", | |||
"DataScienceSurveyBanner.bannerMessage": "Can you please take 2 minutes to tell us how the Python Data Science features are working for you?", | |||
"DataScienceSurveyBanner.bannerLabelYes": "Yes, take survey now", | |||
"DataScienceSurveyBanner.bannerLabelNo": "No, thanks", | |||
"InteractiveShiftEnterBanner.bannerMessage": "Would you like shift-enter to send code to the new Interactive Window experience?", | |||
"InteractiveShiftEnterBanner.bannerLabelYes": "Yes", | |||
"InteractiveShiftEnterBanner.bannerLabelNo": "No", | |||
"DataScience.restartingKernelStatus": "Restarting iPython Kernel", | |||
"DataScience.executingCode": "Executing Cell", | |||
"DataScience.collapseAll": "Collapse all cell inputs", | |||
"DataScience.expandAll": "Expand all cell inputs", | |||
"DataScience.export": "Export as Jupyter Notebook", | |||
"DataScience.restartServer": "Restart iPython Kernel", | |||
"DataScience.undo": "Undo", | |||
"DataScience.redo": "Redo", | |||
"DataScience.clearAll": "Remove All Cells", | |||
"DataScience.pythonVersionHeader": "Python version:", | |||
"DataScience.pythonVersionHeaderNoPyKernel": "Python version may not match, no ipykernel found:", | |||
"DataScience.pythonRestartHeader": "Restarted kernel:", | |||
"DataScience.pythonNewHeader": "Started new kernel:", | |||
"DataScience.executingCodeFailure": "Executing code failed : {0}", | |||
"DataScience.inputWatermark": "Shift-enter to run", | |||
"Linter.InstalledButNotEnabled": "Linter {0} is installed but not enabled.", | |||
"Linter.replaceWithSelectedLinter": "Multiple linters are enabled in settings. Replace with '{0}'?", | |||
"DataScience.jupyterSelectURILaunchLocal": "Launch a local Jupyter server when needed", | |||
"DataScience.jupyterSelectURISpecifyURI": "Type in the URI to connect to a running Jupyter server", | |||
"DataScience.jupyterSelectURIPrompt": "Enter the URI of a Jupyter server", | |||
"DataScience.jupyterSelectURIInvalidURI": "Invalid URI specified", | |||
"DataScience.jupyterNotebookFailure": "Jupyter notebook failed to launch. \r\n{0}", | |||
"DataScience.jupyterNotebookConnectFailed": "Failed to connect to Jupyter notebook. \r\n{0}\r\n{1}", | |||
"DataScience.jupyterNotebookRemoteConnectFailed": "Failed to connect to remote Jupyter notebook.\r\nCheck that the Jupyter Server URI setting has a valid running server specified.\r\n{0}\r\n{1}", | |||
"DataScience.notebookVersionFormat": "Jupyter Notebook Version: {0}", | |||
"DataScience.jupyterKernelNotSupportedOnActive": "Jupyter kernel cannot be started from '{0}'. Using closest match {1} instead.", | |||
"DataScience.jupyterKernelSpecNotFound": "Cannot create a Jupyter kernel spec and none are available for use", | |||
"DataScience.jupyterGetVariablesBadResults": "Failed to fetch variable info from the Jupyter server.", | |||
"DataScience.liveShareConnectFailure": "Cannot connect to host Jupyter session. URI not found.", | |||
"DataScience.liveShareCannotSpawnNotebooks": "Spawning Jupyter notebooks is not supported over a live share connection", | |||
"DataScience.liveShareCannotImportNotebooks": "Importing notebooks is not currently supported over a live share connection", | |||
"DataScience.liveShareHostFormat": "{0} Jupyter Server", | |||
"DataScience.liveShareSyncFailure": "Synchronization failure during live share startup.", | |||
"DataScience.liveShareServiceFailure": "Failure starting '{0}' service during live share connection.", | |||
"DataScience.documentMismatch": "Cannot run cells, duplicate documents for {0} found.", | |||
"DataScience.pythonInteractiveCreateFailed": "Failure to create a 'Python Interactive' window. Try reinstalling the Python extension.", | |||
"diagnostics.warnSourceMaps": "Source map support is enabled in the Python Extension, this will adversely impact performance of the extension.", | |||
"diagnostics.disableSourceMaps": "Disable Source Map Support", | |||
"diagnostics.warnBeforeEnablingSourceMaps": "Enabling source map support in the Python Extension will adversely impact performance of the extension.", | |||
"diagnostics.enableSourceMapsAndReloadVSC": "Enable and reload Window", | |||
"diagnostics.lsNotSupported": "Your operating system does not meet the minimum requirements of the Language Server. Reverting to the alternative, Jedi.", | |||
"diagnostics.invalidPythonPathInDebuggerSettings": "You need to select a Python interpreter before you start debugging.\n\nTip: click on \"Select Python Interpreter\" in the status bar.", | |||
"diagnostics.invalidPythonPathInDebuggerLaunch": "The Python path in your debug configuration is invalid.", | |||
"DataScience.interruptKernel": "Interrupt iPython Kernel", | |||
"DataScience.exportingFormat": "Exporting {0}", | |||
"DataScience.exportCancel": "Cancel", | |||
"Common.canceled": "Canceled", | |||
"DataScience.importChangeDirectoryComment": "#%% Change working directory from the workspace root to the ipynb file location. Turn this addition off with the DataScience.changeDirOnImportExport setting", | |||
"DataScience.exportChangeDirectoryComment": "# Change directory to VSCode workspace root so that relative path loads work correctly. Turn this addition off with the DataScience.changeDirOnImportExport setting", | |||
"DataScience.interruptKernelStatus": "Interrupting iPython Kernel", | |||
"DataScience.restartKernelAfterInterruptMessage": "Interrupting the kernel timed out. Do you want to restart the kernel instead? All variables will be lost.", | |||
"DataScience.pythonInterruptFailedHeader": "Keyboard interrupt crashed the kernel. Kernel restarted.", | |||
"DataScience.sysInfoURILabel": "Jupyter Server URI: ", | |||
"Common.loadingPythonExtension": "Python extension loading...", | |||
"debug.selectConfigurationTitle": "Select a debug configuration", | |||
"debug.selectConfigurationPlaceholder": "Debug Configuration", | |||
"debug.debugFileConfigurationLabel": "Python File", | |||
"debug.debugFileConfigurationDescription": "Debug Python file", | |||
"debug.debugModuleConfigurationLabel": "Module", | |||
"debug.debugModuleConfigurationDescription": "Debug Python module/package", | |||
"debug.remoteAttachConfigurationLabel": "Remote Attach", | |||
"debug.remoteAttachConfigurationDescription": "Debug a remote Python program", | |||
"debug.debugDjangoConfigurationLabel": "Django", | |||
"debug.debugDjangoConfigurationDescription": "Web Application", | |||
"debug.debugFlaskConfigurationLabel": "Flask", | |||
"debug.debugFlaskConfigurationDescription": "Web Application", | |||
"debug.debugPyramidConfigurationLabel": "Pyramid", | |||
"debug.debugPyramidConfigurationDescription": "Web Application", | |||
"debug.djangoEnterManagePyPathTitle": "Debug Django", | |||
"debug.djangoEnterManagePyPathPrompt": "Enter path to manage.py ('${workspaceFolderToken}' points to the root of the current workspace folder)", | |||
"debug.djangoEnterManagePyPathInvalidFilePathError": "Enter a valid Python file path", | |||
"debug.flaskEnterAppPathOrNamePathTitle": "Debug Flask", | |||
"debug.flaskEnterAppPathOrNamePathPrompt": "Enter path to application, e.g. 'app.py' or 'app'", | |||
"debug.flaskEnterAppPathOrNamePathInvalidNameError": "Enter a valid name", | |||
"debug.moduleEnterModuleTitle": "Debug Module", | |||
"debug.moduleEnterModulePrompt": "Enter Python module/package name", | |||
"debug.moduleEnterModuleInvalidNameError": "Enter a valid name", | |||
"debug.pyramidEnterDevelopmentIniPathTitle": "Debug Pyramid", | |||
"debug.pyramidEnterDevelopmentIniPathPrompt": "`Enter path to development.ini ('${workspaceFolderToken}' points to the root of the current workspace folder)`", | |||
"debug.pyramidEnterDevelopmentIniPathInvalidFilePathError": "Enter a valid file path", | |||
"debug.attachRemotePortTitle": "Remote Debugging", | |||
"debug.attachRemotePortPrompt": "Enter port number", | |||
"debug.attachRemotePortValidationError": "Enter a valid port number", | |||
"debug.attachRemoteHostTitle": "Remote Debugging", | |||
"debug.attachRemoteHostPrompt": "Enter a host name or IP address", | |||
"debug.attachRemoteHostValidationError": "Enter a valid host name or IP address", | |||
"UnitTests.testErrorDiagnosticMessage": "Error", | |||
"UnitTests.testFailDiagnosticMessage": "Fail", | |||
"UnitTests.testSkippedDiagnosticMessage": "Skipped", | |||
"UnitTests.configureTests": "Configure Test Framework", | |||
"UnitTests.disableTests": "Disable Tests", | |||
"Common.openOutputPanel": "Show output", | |||
"LanguageService.downloadFailedOutputMessage": "download failed", | |||
"LanguageService.extractionFailedOutputMessage": "extraction failed", | |||
"LanguageService.extractionCompletedOutputMessage": "complete", | |||
"LanguageService.extractionDoneOutputMessage": "done", | |||
"LanguageService.reloadVSCodeIfSeachPathHasChanged": "Search paths have changed for this Python interpreter. Please reload the extension to ensure that the IntelliSense works correctly" | |||
} |
@@ -1,709 +0,0 @@ | |||
import os | |||
import os.path | |||
import io | |||
import re | |||
import sys | |||
import json | |||
import traceback | |||
import platform | |||
jediPreview = False | |||
class RedirectStdout(object): | |||
def __init__(self, new_stdout=None): | |||
"""If stdout is None, redirect to /dev/null""" | |||
self._new_stdout = new_stdout or open(os.devnull, "w") | |||
def __enter__(self): | |||
sys.stdout.flush() | |||
self.oldstdout_fno = os.dup(sys.stdout.fileno()) | |||
os.dup2(self._new_stdout.fileno(), 1) | |||
def __exit__(self, exc_type, exc_value, traceback): | |||
self._new_stdout.flush() | |||
os.dup2(self.oldstdout_fno, 1) | |||
os.close(self.oldstdout_fno) | |||
class JediCompletion(object): | |||
basic_types = { | |||
"module": "import", | |||
"instance": "variable", | |||
"statement": "value", | |||
"param": "variable", | |||
} | |||
def __init__(self): | |||
self.default_sys_path = sys.path | |||
self.environment = jedi.api.environment.create_environment( | |||
sys.executable, safe=False | |||
) | |||
self._input = io.open(sys.stdin.fileno(), encoding="utf-8") | |||
if (os.path.sep == "/") and (platform.uname()[2].find("Microsoft") > -1): | |||
# WSL; does not support UNC paths | |||
self.drive_mount = "/mnt/" | |||
elif sys.platform == "cygwin": | |||
# cygwin | |||
self.drive_mount = "/cygdrive/" | |||
else: | |||
# Do no normalization, e.g. Windows build of Python. | |||
# Could add additional test: ((os.path.sep == '/') and os.path.isdir('/mnt/c')) | |||
# However, this may have more false positives trying to identify Windows/*nix hybrids | |||
self.drive_mount = "" | |||
def _get_definition_type(self, definition): | |||
# if definition.type not in ['import', 'keyword'] and is_built_in(): | |||
# return 'builtin' | |||
try: | |||
if definition.type in ["statement"] and definition.name.isupper(): | |||
return "constant" | |||
return self.basic_types.get(definition.type, definition.type) | |||
except Exception: | |||
return "builtin" | |||
def _additional_info(self, completion): | |||
"""Provide additional information about the completion object.""" | |||
if not hasattr(completion, "_definition") or completion._definition is None: | |||
return "" | |||
if completion.type == "statement": | |||
nodes_to_display = ["InstanceElement", "String", "Node", "Lambda", "Number"] | |||
return "".join( | |||
c.get_code() | |||
for c in completion._definition.children | |||
if type(c).__name__ in nodes_to_display | |||
).replace("\n", "") | |||
return "" | |||
@classmethod | |||
def _get_top_level_module(cls, path): | |||
"""Recursively walk through directories looking for top level module. | |||
Jedi will use current filepath to look for another modules at same | |||
path, but it will not be able to see modules **above**, so our goal | |||
is to find the higher python module available from filepath. | |||
""" | |||
_path, _ = os.path.split(path) | |||
if os.path.isfile(os.path.join(_path, "__init__.py")): | |||
return cls._get_top_level_module(_path) | |||
return path | |||
def _generate_signature(self, completion): | |||
"""Generate signature with function arguments. | |||
""" | |||
if completion.type in ["module"] or not hasattr(completion, "params"): | |||
return "" | |||
return "%s(%s)" % ( | |||
completion.name, | |||
", ".join(p.description[6:] for p in completion.params if p), | |||
) | |||
def _get_call_signatures(self, script, line, column): | |||
"""Extract call signatures from jedi.api.Script object in failsafe way. | |||
Returns: | |||
Tuple with original signature object, name and value. | |||
""" | |||
_signatures = [] | |||
try: | |||
call_signatures = script.get_signatures(line, column) | |||
except KeyError: | |||
call_signatures = [] | |||
except: | |||
call_signatures = [] | |||
for signature in call_signatures: | |||
for pos, param in enumerate(signature.params): | |||
if not param.name: | |||
continue | |||
name = self._get_param_name(param) | |||
if param.name == "self" and pos == 0: | |||
continue | |||
if name.startswith("*"): | |||
continue | |||
value = self._get_param_value(param) | |||
_signatures.append((signature, name, value)) | |||
return _signatures | |||
def _get_param_name(self, p): | |||
if p.name.startswith("param "): | |||
return p.name[6:] # drop leading 'param ' | |||
return p.name | |||
def _get_param_value(self, p): | |||
pair = p.description.split("=") | |||
if len(pair) > 1: | |||
return pair[1] | |||
return None | |||
def _get_call_signatures_with_args(self, script, line, column): | |||
"""Extract call signatures from jedi.api.Script object in failsafe way. | |||
Returns: | |||
Array with dictionary | |||
""" | |||
_signatures = [] | |||
try: | |||
call_signatures = script.get_signatures(line, column) | |||
except KeyError: | |||
call_signatures = [] | |||
for signature in call_signatures: | |||
sig = { | |||
"name": "", | |||
"description": "", | |||
"docstring": "", | |||
"paramindex": 0, | |||
"params": [], | |||
"bracketstart": [], | |||
} | |||
sig["description"] = signature.description | |||
try: | |||
sig["docstring"] = signature.docstring() | |||
sig["raw_docstring"] = signature.docstring(raw=True) | |||
except Exception: | |||
sig["docstring"] = "" | |||
sig["raw_docstring"] = "" | |||
sig["name"] = signature.name | |||
sig["paramindex"] = signature.index | |||
sig["bracketstart"].append(signature.index) | |||
_signatures.append(sig) | |||
for pos, param in enumerate(signature.params): | |||
if not param.name: | |||
continue | |||
name = self._get_param_name(param) | |||
if param.name == "self" and pos == 0: | |||
continue | |||
value = self._get_param_value(param) | |||
paramDocstring = "" | |||
try: | |||
paramDocstring = param.docstring() | |||
except Exception: | |||
paramDocstring = "" | |||
sig["params"].append( | |||
{ | |||
"name": name, | |||
"value": value, | |||
"docstring": paramDocstring, | |||
"description": param.description, | |||
} | |||
) | |||
return _signatures | |||
def _serialize_completions(self, script, line, column, identifier=None, prefix=""): | |||
"""Serialize response to be read from VSCode. | |||
Args: | |||
script: Instance of jedi.api.Script object. | |||
identifier: Unique completion identifier to pass back to VSCode. | |||
prefix: String with prefix to filter function arguments. | |||
Used only when fuzzy matcher turned off. | |||
Returns: | |||
Serialized string to send to VSCode. | |||
""" | |||
_completions = [] | |||
for signature, name, value in self._get_call_signatures(script, line, column): | |||
if not self.fuzzy_matcher and not name.lower().startswith(prefix.lower()): | |||
continue | |||
_completion = { | |||
"type": "property", | |||
"raw_type": "", | |||
"rightLabel": self._additional_info(signature), | |||
} | |||
_completion["description"] = "" | |||
_completion["raw_docstring"] = "" | |||
# we pass 'text' here only for fuzzy matcher | |||
if value: | |||
_completion["snippet"] = "%s=${1:%s}$0" % (name, value) | |||
_completion["text"] = "%s=" % (name) | |||
else: | |||
_completion["snippet"] = "%s=$1$0" % name | |||
_completion["text"] = name | |||
_completion["displayText"] = name | |||
_completions.append(_completion) | |||
try: | |||
completions = script.complete(line, column) | |||
except KeyError: | |||
completions = [] | |||
except: | |||
completions = [] | |||
for completion in completions: | |||
try: | |||
_completion = { | |||
"text": completion.name, | |||
"type": self._get_definition_type(completion), | |||
"raw_type": completion.type, | |||
"rightLabel": self._additional_info(completion), | |||
} | |||
except Exception: | |||
continue | |||
for c in _completions: | |||
if c["text"] == _completion["text"]: | |||
c["type"] = _completion["type"] | |||
c["raw_type"] = _completion["raw_type"] | |||
if any( | |||
[c["text"].split("=")[0] == _completion["text"] for c in _completions] | |||
): | |||
# ignore function arguments we already have | |||
continue | |||
_completions.append(_completion) | |||
return json.dumps({"id": identifier, "results": _completions}) | |||
def _serialize_methods(self, script, line, column, identifier=None, prefix=""): | |||
_methods = [] | |||
try: | |||
completions = script.complete(line, column) | |||
except KeyError: | |||
return [] | |||
for completion in completions: | |||
if completion.name == "__autocomplete_python": | |||
instance = completion.parent().name | |||
break | |||
else: | |||
instance = "self.__class__" | |||
for completion in completions: | |||
params = [] | |||
if hasattr(completion, "params"): | |||
params = [p.description for p in completion.params if p] | |||
if completion.parent().type == "class": | |||
_methods.append( | |||
{ | |||
"parent": completion.parent().name, | |||
"instance": instance, | |||
"name": completion.name, | |||
"params": params, | |||
"moduleName": completion.module_name, | |||
"fileName": completion.module_path, | |||
"line": completion.line, | |||
"column": completion.column, | |||
} | |||
) | |||
return json.dumps({"id": identifier, "results": _methods}) | |||
def _serialize_arguments(self, script, line, column, identifier=None): | |||
"""Serialize response to be read from VSCode. | |||
Args: | |||
script: Instance of jedi.api.Script object. | |||
identifier: Unique completion identifier to pass back to VSCode. | |||
Returns: | |||
Serialized string to send to VSCode. | |||
""" | |||
return json.dumps( | |||
{ | |||
"id": identifier, | |||
"results": self._get_call_signatures_with_args(script, line, column), | |||
} | |||
) | |||
def _top_definition(self, definition): | |||
for d in definition.goto_assignments(): | |||
if d == definition: | |||
continue | |||
if d.type == "import": | |||
return self._top_definition(d) | |||
else: | |||
return d | |||
return definition | |||
def _extract_range_jedi_0_11_1(self, definition): | |||
from parso.utils import split_lines | |||
# get the scope range | |||
try: | |||
if definition.type in ["class", "function"]: | |||
tree_name = definition._name.tree_name | |||
scope = tree_name.get_definition() | |||
start_line = scope.start_pos[0] - 1 | |||
start_column = scope.start_pos[1] | |||
# get the lines | |||
code = scope.get_code(include_prefix=False) | |||
lines = split_lines(code) | |||
# trim the lines | |||
lines = "\n".join(lines).rstrip().split("\n") | |||
end_line = start_line + len(lines) - 1 | |||
end_column = len(lines[-1]) - 1 | |||
else: | |||
symbol = definition._name.tree_name | |||
start_line = symbol.start_pos[0] - 1 | |||
start_column = symbol.start_pos[1] | |||
end_line = symbol.end_pos[0] - 1 | |||
end_column = symbol.end_pos[1] | |||
return { | |||
"start_line": start_line, | |||
"start_column": start_column, | |||
"end_line": end_line, | |||
"end_column": end_column, | |||
} | |||
except Exception as e: | |||
return { | |||
"start_line": definition.line - 1, | |||
"start_column": definition.column, | |||
"end_line": definition.line - 1, | |||
"end_column": definition.column, | |||
} | |||
def _extract_range(self, definition): | |||
"""Provides the definition range of a given definition | |||
For regular symbols it returns the start and end location of the | |||
characters making up the symbol. | |||
For scoped containers it will return the entire definition of the | |||
scope. | |||
The scope that jedi provides ends with the first character of the next | |||
scope so it's not ideal. For vscode we need the scope to end with the | |||
last character of actual code. That's why we extract the lines that | |||
make up our scope and trim the trailing whitespace. | |||
""" | |||
return self._extract_range_jedi_0_11_1(definition) | |||
def _get_definitionsx(self, definitions, identifier=None, ignoreNoModulePath=False): | |||
"""Serialize response to be read from VSCode. | |||
Args: | |||
definitions: List of jedi.api.classes.Definition objects. | |||
identifier: Unique completion identifier to pass back to VSCode. | |||
Returns: | |||
Serialized string to send to VSCode. | |||
""" | |||
_definitions = [] | |||
for definition in definitions: | |||
try: | |||
if definition.type == "import": | |||
definition = self._top_definition(definition) | |||
definitionRange = { | |||
"start_line": 0, | |||
"start_column": 0, | |||
"end_line": 0, | |||
"end_column": 0, | |||
} | |||
module_path = "" | |||
if hasattr(definition, "module_path") and definition.module_path: | |||
module_path = definition.module_path | |||
definitionRange = self._extract_range(definition) | |||
else: | |||
if not ignoreNoModulePath: | |||
continue | |||
try: | |||
parent = definition.parent() | |||
container = parent.name if parent.type != "module" else "" | |||
except Exception: | |||
container = "" | |||
try: | |||
docstring = definition.docstring() | |||
rawdocstring = definition.docstring(raw=True) | |||
except Exception: | |||
docstring = "" | |||
rawdocstring = "" | |||
_definition = { | |||
"text": definition.name, | |||
"type": self._get_definition_type(definition), | |||
"raw_type": definition.type, | |||
"fileName": module_path, | |||
"container": container, | |||
"range": definitionRange, | |||
"description": definition.description, | |||
"docstring": docstring, | |||
"raw_docstring": rawdocstring, | |||
"signature": self._generate_signature(definition), | |||
} | |||
_definitions.append(_definition) | |||
except Exception as e: | |||
pass | |||
return _definitions | |||
def _serialize_definitions(self, definitions, identifier=None): | |||
"""Serialize response to be read from VSCode. | |||
Args: | |||
definitions: List of jedi.api.classes.Definition objects. | |||
identifier: Unique completion identifier to pass back to VSCode. | |||
Returns: | |||
Serialized string to send to VSCode. | |||
""" | |||
_definitions = [] | |||
for definition in definitions: | |||
try: | |||
if definition.module_path: | |||
if definition.type == "import": | |||
definition = self._top_definition(definition) | |||
if not definition.module_path: | |||
continue | |||
try: | |||
parent = definition.parent() | |||
container = parent.name if parent.type != "module" else "" | |||
except Exception: | |||
container = "" | |||
try: | |||
docstring = definition.docstring() | |||
rawdocstring = definition.docstring(raw=True) | |||
except Exception: | |||
docstring = "" | |||
rawdocstring = "" | |||
_definition = { | |||
"text": definition.name, | |||
"type": self._get_definition_type(definition), | |||
"raw_type": definition.type, | |||
"fileName": definition.module_path, | |||
"container": container, | |||
"range": self._extract_range(definition), | |||
"description": definition.description, | |||
"docstring": docstring, | |||
"raw_docstring": rawdocstring, | |||
} | |||
_definitions.append(_definition) | |||
except Exception as e: | |||
pass | |||
return json.dumps({"id": identifier, "results": _definitions}) | |||
def _serialize_tooltip(self, definitions, identifier=None): | |||
_definitions = [] | |||
for definition in definitions: | |||
signature = definition.name | |||
description = None | |||
if definition.type in ["class", "function"]: | |||
signature = self._generate_signature(definition) | |||
try: | |||
description = definition.docstring(raw=True).strip() | |||
except Exception: | |||
description = "" | |||
if not description and not hasattr(definition, "get_line_code"): | |||
# jedi returns an empty string for compiled objects | |||
description = definition.docstring().strip() | |||
if definition.type == "module": | |||
signature = definition.full_name | |||
try: | |||
description = definition.docstring(raw=True).strip() | |||
except Exception: | |||
description = "" | |||
if not description and hasattr(definition, "get_line_code"): | |||
# jedi returns an empty string for compiled objects | |||
description = definition.docstring().strip() | |||
_definition = { | |||
"type": self._get_definition_type(definition), | |||
"text": definition.name, | |||
"description": description, | |||
"docstring": description, | |||
"signature": signature, | |||
} | |||
_definitions.append(_definition) | |||
return json.dumps({"id": identifier, "results": _definitions}) | |||
def _serialize_usages(self, usages, identifier=None): | |||
_usages = [] | |||
for usage in usages: | |||
_usages.append( | |||
{ | |||
"name": usage.name, | |||
"moduleName": usage.module_name, | |||
"fileName": usage.module_path, | |||
"line": usage.line, | |||
"column": usage.column, | |||
} | |||
) | |||
return json.dumps({"id": identifier, "results": _usages}) | |||
def _deserialize(self, request): | |||
"""Deserialize request from VSCode. | |||
Args: | |||
request: String with raw request from VSCode. | |||
Returns: | |||
Python dictionary with request data. | |||
""" | |||
return json.loads(request) | |||
def _set_request_config(self, config): | |||
"""Sets config values for current request. | |||
This includes sys.path modifications which is getting restored to | |||
default value on each request so each project should be isolated | |||
from each other. | |||
Args: | |||
config: Dictionary with config values. | |||
""" | |||
sys.path = self.default_sys_path | |||
self.use_snippets = config.get("useSnippets") | |||
self.show_doc_strings = config.get("showDescriptions", True) | |||
self.fuzzy_matcher = config.get("fuzzyMatcher", False) | |||
jedi.settings.case_insensitive_completion = config.get( | |||
"caseInsensitiveCompletion", True | |||
) | |||
for path in config.get("extraPaths", []): | |||
if path and path not in sys.path: | |||
sys.path.insert(0, path) | |||
def _normalize_request_path(self, request): | |||
"""Normalize any Windows paths received by a *nix build of | |||
Python. Does not alter the reverse os.path.sep=='\\', | |||
i.e. *nix paths received by a Windows build of Python. | |||
""" | |||
if "path" in request: | |||
if not self.drive_mount: | |||
return | |||
newPath = request["path"].replace("\\", "/") | |||
if newPath[0:1] == "/": | |||
# is absolute path with no drive letter | |||
request["path"] = newPath | |||
elif newPath[1:2] == ":": | |||
# is path with drive letter, only absolute can be mapped | |||
request["path"] = self.drive_mount + newPath[0:1].lower() + newPath[2:] | |||
else: | |||
# is relative path | |||
request["path"] = newPath | |||
def _process_request(self, request): | |||
"""Accept serialized request from VSCode and write response. | |||
""" | |||
request = self._deserialize(request) | |||
self._set_request_config(request.get("config", {})) | |||
self._normalize_request_path(request) | |||
path = self._get_top_level_module(request.get("path", "")) | |||
if len(path) > 0 and path not in sys.path: | |||
sys.path.insert(0, path) | |||
lookup = request.get("lookup", "completions") | |||
if lookup == "names": | |||
return self._serialize_definitions( | |||
jedi.Script( | |||
code=request.get("source", None), | |||
path=request.get("path", ""), | |||
project=jedi.get_default_project(os.path.dirname(path)), | |||
environment=self.environment, | |||
).get_names(all_scopes=True), | |||
request["id"], | |||
) | |||
line = request["line"] + 1 | |||
column = request["column"] | |||
script = jedi.Script( | |||
code=request.get("source", None), | |||
path=request.get("path", ""), | |||
project=jedi.get_default_project(os.path.dirname(path)), | |||
environment=self.environment, | |||
) | |||
if lookup == "definitions": | |||
defs = self._get_definitionsx( | |||
script.goto(line, column, follow_imports=True), request["id"] | |||
) | |||
return json.dumps({"id": request["id"], "results": defs}) | |||
if lookup == "tooltip": | |||
if jediPreview: | |||
defs = [] | |||
try: | |||
defs = self._get_definitionsx( | |||
script.infer(line, column), request["id"], True | |||
) | |||
except: | |||
pass | |||
try: | |||
if len(defs) == 0: | |||
defs = self._get_definitionsx( | |||
script.goto(line, column), request["id"], True | |||
) | |||
except: | |||
pass | |||
return json.dumps({"id": request["id"], "results": defs}) | |||
else: | |||
try: | |||
return self._serialize_tooltip( | |||
script.infer(line, column), request["id"] | |||
) | |||
except: | |||
return json.dumps({"id": request["id"], "results": []}) | |||
elif lookup == "arguments": | |||
return self._serialize_arguments(script, line, column, request["id"]) | |||
elif lookup == "usages": | |||
return self._serialize_usages( | |||
script.get_references(line, column), request["id"] | |||
) | |||
elif lookup == "methods": | |||
return self._serialize_methods( | |||
script, line, column, request["id"], request.get("prefix", "") | |||
) | |||
else: | |||
return self._serialize_completions( | |||
script, line, column, request["id"], request.get("prefix", "") | |||
) | |||
def _write_response(self, response): | |||
sys.stdout.write(response + "\n") | |||
sys.stdout.flush() | |||
def watch(self): | |||
while True: | |||
try: | |||
rq = self._input.readline() | |||
if len(rq) == 0: | |||
# Reached EOF - indication our parent process is gone. | |||
sys.stderr.write( | |||
"Received EOF from the standard input,exiting" + "\n" | |||
) | |||
sys.stderr.flush() | |||
return | |||
with RedirectStdout(): | |||
response = self._process_request(rq) | |||
self._write_response(response) | |||
except Exception: | |||
sys.stderr.write(traceback.format_exc() + "\n") | |||
sys.stderr.flush() | |||
if __name__ == "__main__": | |||
cachePrefix = "v" | |||
modulesToLoad = "" | |||
if len(sys.argv) > 2 and sys.argv[1] == "custom": | |||
jediPath = sys.argv[2] | |||
jediPreview = True | |||
cachePrefix = "custom_v" | |||
if len(sys.argv) > 3: | |||
modulesToLoad = sys.argv[3] | |||
else: | |||
# release | |||
jediPath = os.path.join(os.path.dirname(__file__), "lib", "python") | |||
if len(sys.argv) > 1: | |||
modulesToLoad = sys.argv[1] | |||
sys.path.insert(0, jediPath) | |||
import jedi | |||
digits = jedi.__version__.split(".") | |||
if int(digits[0]) == 0 and int(digits[1]) < 17: | |||
raise RuntimeError("Jedi version %s too old, requires >= 0.17.0" % (jedi.__version__)) | |||
else: | |||
if jediPreview: | |||
jedi.settings.cache_directory = os.path.join( | |||
jedi.settings.cache_directory, | |||
cachePrefix + jedi.__version__.replace(".", ""), | |||
) | |||
# remove jedi from path after we import it so it will not be completed | |||
sys.path.pop(0) | |||
if len(modulesToLoad) > 0: | |||
jedi.preload_module(*modulesToLoad.split(",")) | |||
JediCompletion().watch() |
@@ -1,25 +0,0 @@ | |||
# This file can mimic juypter running. Useful for testing jupyter crash handling | |||
import sys | |||
import argparse | |||
import time | |||
def main(): | |||
print('hello from dummy jupyter') | |||
parser = argparse.ArgumentParser() | |||
parser.add_argument('--version', type=bool, default=False, const=True, nargs='?') | |||
parser.add_argument('notebook', type=bool, default=False, const=True, nargs='?') | |||
parser.add_argument('--no-browser', type=bool, default=False, const=True, nargs='?') | |||
parser.add_argument('--notebook-dir', default='') | |||
parser.add_argument('--config', default='') | |||
results = parser.parse_args() | |||
if (results.version): | |||
print('1.1.dummy') | |||
else: | |||
print('http://localhost:8888/?token=012f08663a68e279fe0a5335e0b5dfe44759ddcccf0b3a56') | |||
time.sleep(5) | |||
raise Exception('Dummy is dead') | |||
if __name__ == '__main__': | |||
main() |
@@ -1,13 +0,0 @@ | |||
# Query Jupyter server for defined variables list | |||
# Tested on 2.7 and 3.6 | |||
from sys import getsizeof | |||
import json | |||
# who_ls is a Jupyter line magic to fetch currently defined vars | |||
_VSCode_JupyterVars = %who_ls | |||
print(json.dumps([{'name': var, | |||
'type': type(eval(var)).__name__, | |||
'size': getsizeof(var), | |||
'expensive': True | |||
} for var in _VSCode_JupyterVars])) |
@@ -1,25 +0,0 @@ | |||
# Query Jupyter server for the value of a variable | |||
import json | |||
_VSCODE_max_len = 200 | |||
# In IJupyterVariables.getValue this '_VSCode_JupyterTestValue' will be replaced with the json stringified value of the target variable | |||
# Indexes off of _VSCODE_targetVariable need to index types that are part of IJupyterVariable | |||
_VSCODE_targetVariable = json.loads('_VSCode_JupyterTestValue') | |||
_VSCODE_evalResult = eval(_VSCODE_targetVariable['name']) | |||
# Find shape and count if available | |||
if _VSCODE_targetVariable['type'] in ['ndarray','DataFrame','Series']: | |||
_VSCODE_targetVariable['shape'] = str(_VSCODE_evalResult.shape) | |||
if _VSCODE_targetVariable['type'] in ['tuple', 'str', 'dict', 'list', 'set', 'ndarray','DataFrame','Series']: | |||
_VSCODE_targetVariable['count'] = len(_VSCODE_evalResult) | |||
# Get the string of the eval result, truncate it as it could be far too long | |||
_VSCODE_targetValue = str(_VSCODE_evalResult) | |||
if len(_VSCODE_targetValue) > _VSCODE_max_len: | |||
_VSCODE_targetVariable['truncated'] = True | |||
_VSCODE_targetVariable['value'] = _VSCODE_targetValue[:_VSCODE_max_len] | |||
else: | |||
_VSCODE_targetVariable['value'] = _VSCODE_targetValue | |||
print(json.dumps(_VSCODE_targetVariable)) |
@@ -1,24 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
from notebook.notebookapp import list_running_servers | |||
import json | |||
server_list = list_running_servers() | |||
server_info_list = [] | |||
for si in server_list: | |||
server_info_object = {} | |||
server_info_object["base_url"] = si['base_url'] | |||
server_info_object["notebook_dir"] = si['notebook_dir'] | |||
server_info_object["hostname"] = si['hostname'] | |||
server_info_object["password"] = si['password'] | |||
server_info_object["pid"] = si['pid'] | |||
server_info_object["port"] = si['port'] | |||
server_info_object["secure"] = si['secure'] | |||
server_info_object["token"] = si['token'] | |||
server_info_object["url"] = si['url'] | |||
server_info_list.append(server_info_object) | |||
print(json.dumps(server_info_list)) |
@@ -1,13 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
import json | |||
import sys | |||
obj = {} | |||
obj["versionInfo"] = sys.version_info[:4] | |||
obj["sysPrefix"] = sys.prefix | |||
obj["version"] = sys.version | |||
obj["is64Bit"] = sys.maxsize > 2**32 | |||
print(json.dumps(obj)) |
@@ -1,133 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
import ast | |||
import io | |||
import operator | |||
import os | |||
import sys | |||
import token | |||
import tokenize | |||
class Visitor(ast.NodeVisitor): | |||
def __init__(self, lines): | |||
self._lines = lines | |||
self.line_numbers_with_nodes = set() | |||
self.line_numbers_with_statements = [] | |||
def generic_visit(self, node): | |||
if hasattr(node, 'col_offset') and hasattr(node, 'lineno') and node.col_offset == 0: | |||
self.line_numbers_with_nodes.add(node.lineno) | |||
if isinstance(node, ast.stmt): | |||
self.line_numbers_with_statements.append(node.lineno) | |||
ast.NodeVisitor.generic_visit(self, node) | |||
def _tokenize(source): | |||
"""Tokenize Python source code.""" | |||
# Using an undocumented API as the documented one in Python 2.7 does not work as needed | |||
# cross-version. | |||
if sys.version_info < (3,) and isinstance(source, str): | |||
source = source.decode() | |||
return tokenize.generate_tokens(io.StringIO(source).readline) | |||
def _indent_size(line): | |||
for index, char in enumerate(line): | |||
if not char.isspace(): | |||
return index | |||
def _get_global_statement_blocks(source, lines): | |||
"""Return a list of all global statement blocks. | |||
The list comprises of 3-item tuples that contain the starting line number, | |||
ending line number and whether the statement is a single line. | |||
""" | |||
tree = ast.parse(source) | |||
visitor = Visitor(lines) | |||
visitor.visit(tree) | |||
statement_ranges = [] | |||
for index, line_number in enumerate(visitor.line_numbers_with_statements): | |||
remaining_line_numbers = visitor.line_numbers_with_statements[index+1:] | |||
end_line_number = len(lines) if len(remaining_line_numbers) == 0 else min(remaining_line_numbers) - 1 | |||
current_statement_is_oneline = line_number == end_line_number | |||
if len(statement_ranges) == 0: | |||
statement_ranges.append((line_number, end_line_number, current_statement_is_oneline)) | |||
continue | |||
previous_statement = statement_ranges[-1] | |||
previous_statement_is_oneline = previous_statement[2] | |||
if previous_statement_is_oneline and current_statement_is_oneline: | |||
statement_ranges[-1] = previous_statement[0], end_line_number, True | |||
else: | |||
statement_ranges.append((line_number, end_line_number, current_statement_is_oneline)) | |||
return statement_ranges | |||
def normalize_lines(source): | |||
"""Normalize blank lines for sending to the terminal. | |||
Blank lines within a statement block are removed to prevent the REPL | |||
from thinking the block is finished. Newlines are added to separate | |||
top-level statements so that the REPL does not think there is a syntax | |||
error. | |||
""" | |||
lines = source.splitlines(False) | |||
# If we have two blank lines, then add two blank lines. | |||
# Do not trim the spaces, if we have blank lines with spaces, its possible | |||
# we have indented code. | |||
if (len(lines) > 1 and len(''.join(lines[-2:])) == 0) \ | |||
or source.endswith(('\n\n', '\r\n\r\n')): | |||
trailing_newline = '\n' * 2 | |||
# Find out if we have any trailing blank lines | |||
elif len(lines[-1].strip()) == 0 or source.endswith(('\n', '\r\n')): | |||
trailing_newline = '\n' | |||
else: | |||
trailing_newline = '' | |||
# Step 1: Remove empty lines. | |||
tokens = _tokenize(source) | |||
newlines_indexes_to_remove = (spos[0] for (toknum, tokval, spos, epos, line) in tokens | |||
if len(line.strip()) == 0 | |||
and token.tok_name[toknum] == 'NL' | |||
and spos[0] == epos[0]) | |||
for line_number in reversed(list(newlines_indexes_to_remove)): | |||
del lines[line_number-1] | |||
# Step 2: Add blank lines between each global statement block. | |||
# A consequtive single lines blocks of code will be treated as a single statement, | |||
# just to ensure we do not unnecessarily add too many blank lines. | |||
source = '\n'.join(lines) | |||
tokens = _tokenize(source) | |||
dedent_indexes = (spos[0] for (toknum, tokval, spos, epos, line) in tokens | |||
if toknum == token.DEDENT and _indent_size(line) == 0) | |||
global_statement_ranges = _get_global_statement_blocks(source, lines) | |||
start_positions = map(operator.itemgetter(0), reversed(global_statement_ranges)) | |||
for line_number in filter(lambda x: x > 1, start_positions): | |||
lines.insert(line_number-1, '') | |||
sys.stdout.write('\n'.join(lines) + trailing_newline) | |||
sys.stdout.flush() | |||
if __name__ == '__main__': | |||
contents = sys.argv[1] | |||
try: | |||
default_encoding = sys.getdefaultencoding() | |||
encoded_contents = contents.encode(default_encoding, 'surrogateescape') | |||
contents = encoded_contents.decode(default_encoding, 'replace') | |||
except (UnicodeError, LookupError): | |||
pass | |||
if isinstance(contents, bytes): | |||
contents = contents.decode('utf8') | |||
normalize_lines(contents) |
@@ -1,7 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
import os | |||
import json | |||
print(json.dumps(dict(os.environ))) |
@@ -1,45 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
import os | |||
import os.path | |||
import sys | |||
import traceback | |||
useCustomPtvsd = sys.argv[1] == '--custom' | |||
ptvsdArgs = sys.argv[:] | |||
ptvsdArgs.pop(1) | |||
# Load the debugger package | |||
try: | |||
ptvs_lib_path = os.path.join(os.path.dirname(__file__), 'lib', 'python') | |||
if useCustomPtvsd: | |||
sys.path.append(ptvs_lib_path) | |||
else: | |||
sys.path.insert(0, ptvs_lib_path) | |||
try: | |||
import ptvsd | |||
import ptvsd.debugger as vspd | |||
from ptvsd.__main__ import main | |||
ptvsd_loaded = True | |||
except ImportError: | |||
ptvsd_loaded = False | |||
raise | |||
vspd.DONT_DEBUG.append(os.path.normcase(__file__)) | |||
except: | |||
traceback.print_exc() | |||
print(''' | |||
Internal error detected. Please copy the above traceback and report at | |||
https://github.com/Microsoft/vscode-python/issues/new | |||
Press Enter to close. . .''') | |||
try: | |||
raw_input() | |||
except NameError: | |||
input() | |||
sys.exit(1) | |||
finally: | |||
if ptvs_lib_path: | |||
sys.path.remove(ptvs_lib_path) | |||
main(ptvsdArgs) |
@@ -1,303 +0,0 @@ | |||
# Arguments are: | |||
# 1. Working directory. | |||
# 2. Rope folder | |||
import difflib | |||
import io | |||
import json | |||
import os | |||
import sys | |||
import traceback | |||
try: | |||
import rope | |||
from rope.base import libutils | |||
from rope.refactor.rename import Rename | |||
from rope.refactor.extract import ExtractMethod, ExtractVariable | |||
import rope.base.project | |||
import rope.base.taskhandle | |||
except: | |||
jsonMessage = {'error': True, 'message': 'Rope not installed', 'traceback': '', 'type': 'ModuleNotFoundError'} | |||
sys.stderr.write(json.dumps(jsonMessage)) | |||
sys.stderr.flush() | |||
WORKSPACE_ROOT = sys.argv[1] | |||
ROPE_PROJECT_FOLDER = '.vim/.ropeproject' | |||
class RefactorProgress(): | |||
""" | |||
Refactor progress information | |||
""" | |||
def __init__(self, name='Task Name', message=None, percent=0): | |||
self.name = name | |||
self.message = message | |||
self.percent = percent | |||
class ChangeType(): | |||
""" | |||
Change Type Enum | |||
""" | |||
EDIT = 0 | |||
NEW = 1 | |||
DELETE = 2 | |||
class Change(): | |||
""" | |||
""" | |||
EDIT = 0 | |||
NEW = 1 | |||
DELETE = 2 | |||
def __init__(self, filePath, fileMode=ChangeType.EDIT, diff=""): | |||
self.filePath = filePath | |||
self.diff = diff | |||
self.fileMode = fileMode | |||
def get_diff(changeset): | |||
"""This is a copy of the code form the ChangeSet.get_description method found in Rope.""" | |||
new = changeset.new_contents | |||
old = changeset.old_contents | |||
if old is None: | |||
if changeset.resource.exists(): | |||
old = changeset.resource.read() | |||
else: | |||
old = '' | |||
# Ensure code has a trailing empty lines, before generating a diff. | |||
# https://github.com/Microsoft/vscode-python/issues/695. | |||
old_lines = old.splitlines(True) | |||
if not old_lines[-1].endswith('\n'): | |||
old_lines[-1] = old_lines[-1] + os.linesep | |||
new = new + os.linesep | |||
result = difflib.unified_diff( | |||
old_lines, new.splitlines(True), | |||
'a/' + changeset.resource.path, 'b/' + changeset.resource.path) | |||
return ''.join(list(result)) | |||
class BaseRefactoring(object): | |||
""" | |||
Base class for refactorings | |||
""" | |||
def __init__(self, project, resource, name="Refactor", progressCallback=None): | |||
self._progressCallback = progressCallback | |||
self._handle = rope.base.taskhandle.TaskHandle(name) | |||
self._handle.add_observer(self._update_progress) | |||
self.project = project | |||
self.resource = resource | |||
self.changes = [] | |||
def _update_progress(self): | |||
jobset = self._handle.current_jobset() | |||
if jobset and not self._progressCallback is None: | |||
progress = RefactorProgress() | |||
# getting current job set name | |||
if jobset.get_name() is not None: | |||
progress.name = jobset.get_name() | |||
# getting active job name | |||
if jobset.get_active_job_name() is not None: | |||
progress.message = jobset.get_active_job_name() | |||
# adding done percent | |||
percent = jobset.get_percent_done() | |||
if percent is not None: | |||
progress.percent = percent | |||
if not self._progressCallback is None: | |||
self._progressCallback(progress) | |||
def stop(self): | |||
self._handle.stop() | |||
def refactor(self): | |||
try: | |||
self.onRefactor() | |||
except rope.base.exceptions.InterruptedTaskError: | |||
# we can ignore this exception, as user has cancelled refactoring | |||
pass | |||
def onRefactor(self): | |||
""" | |||
To be implemented by each base class | |||
""" | |||
pass | |||
class RenameRefactor(BaseRefactoring): | |||
def __init__(self, project, resource, name="Rename", progressCallback=None, startOffset=None, newName="new_Name"): | |||
BaseRefactoring.__init__(self, project, resource, | |||
name, progressCallback) | |||
self._newName = newName | |||
self.startOffset = startOffset | |||
def onRefactor(self): | |||
renamed = Rename(self.project, self.resource, self.startOffset) | |||
changes = renamed.get_changes(self._newName, task_handle=self._handle) | |||
for item in changes.changes: | |||
if isinstance(item, rope.base.change.ChangeContents): | |||
self.changes.append( | |||
Change(item.resource.real_path, ChangeType.EDIT, get_diff(item))) | |||
else: | |||
raise Exception('Unknown Change') | |||
class ExtractVariableRefactor(BaseRefactoring): | |||
def __init__(self, project, resource, name="Extract Variable", progressCallback=None, startOffset=None, endOffset=None, newName="new_Name", similar=False, global_=False): | |||
BaseRefactoring.__init__(self, project, resource, | |||
name, progressCallback) | |||
self._newName = newName | |||
self._startOffset = startOffset | |||
self._endOffset = endOffset | |||
self._similar = similar | |||
self._global = global_ | |||
def onRefactor(self): | |||
renamed = ExtractVariable( | |||
self.project, self.resource, self._startOffset, self._endOffset) | |||
changes = renamed.get_changes( | |||
self._newName, self._similar, self._global) | |||
for item in changes.changes: | |||
if isinstance(item, rope.base.change.ChangeContents): | |||
self.changes.append( | |||
Change(item.resource.real_path, ChangeType.EDIT, get_diff(item))) | |||
else: | |||
raise Exception('Unknown Change') | |||
class ExtractMethodRefactor(ExtractVariableRefactor): | |||
def __init__(self, project, resource, name="Extract Method", progressCallback=None, startOffset=None, endOffset=None, newName="new_Name", similar=False, global_=False): | |||
ExtractVariableRefactor.__init__(self, project, resource, | |||
name, progressCallback, startOffset=startOffset, endOffset=endOffset, newName=newName, similar=similar, global_=global_) | |||
def onRefactor(self): | |||
renamed = ExtractMethod( | |||
self.project, self.resource, self._startOffset, self._endOffset) | |||
changes = renamed.get_changes( | |||
self._newName, self._similar, self._global) | |||
for item in changes.changes: | |||
if isinstance(item, rope.base.change.ChangeContents): | |||
self.changes.append( | |||
Change(item.resource.real_path, ChangeType.EDIT, get_diff(item))) | |||
else: | |||
raise Exception('Unknown Change') | |||
class RopeRefactoring(object): | |||
def __init__(self): | |||
self.default_sys_path = sys.path | |||
self._input = io.open(sys.stdin.fileno(), encoding='utf-8') | |||
def _rename(self, filePath, start, newName, indent_size): | |||
""" | |||
Renames a variable | |||
""" | |||
project = rope.base.project.Project( | |||
WORKSPACE_ROOT, ropefolder=ROPE_PROJECT_FOLDER, save_history=False, indent_size=indent_size) | |||
resourceToRefactor = libutils.path_to_resource(project, filePath) | |||
refactor = RenameRefactor( | |||
project, resourceToRefactor, startOffset=start, newName=newName) | |||
refactor.refactor() | |||
changes = refactor.changes | |||
project.close() | |||
valueToReturn = [] | |||
for change in changes: | |||
valueToReturn.append({'diff': change.diff}) | |||
return valueToReturn | |||
def _extractVariable(self, filePath, start, end, newName, indent_size): | |||
""" | |||
Extracts a variable | |||
""" | |||
project = rope.base.project.Project( | |||
WORKSPACE_ROOT, ropefolder=ROPE_PROJECT_FOLDER, save_history=False, indent_size=indent_size) | |||
resourceToRefactor = libutils.path_to_resource(project, filePath) | |||
refactor = ExtractVariableRefactor( | |||
project, resourceToRefactor, startOffset=start, endOffset=end, newName=newName, similar=True) | |||
refactor.refactor() | |||
changes = refactor.changes | |||
project.close() | |||
valueToReturn = [] | |||
for change in changes: | |||
valueToReturn.append({'diff': change.diff}) | |||
return valueToReturn | |||
def _extractMethod(self, filePath, start, end, newName, indent_size): | |||
""" | |||
Extracts a method | |||
""" | |||
project = rope.base.project.Project( | |||
WORKSPACE_ROOT, ropefolder=ROPE_PROJECT_FOLDER, save_history=False, indent_size=indent_size) | |||
resourceToRefactor = libutils.path_to_resource(project, filePath) | |||
refactor = ExtractMethodRefactor( | |||
project, resourceToRefactor, startOffset=start, endOffset=end, newName=newName, similar=True) | |||
refactor.refactor() | |||
changes = refactor.changes | |||
project.close() | |||
valueToReturn = [] | |||
for change in changes: | |||
valueToReturn.append({'diff': change.diff}) | |||
return valueToReturn | |||
def _serialize(self, identifier, results): | |||
""" | |||
Serializes the refactor results | |||
""" | |||
return json.dumps({'id': identifier, 'results': results}) | |||
def _deserialize(self, request): | |||
"""Deserialize request from VSCode. | |||
Args: | |||
request: String with raw request from VSCode. | |||
Returns: | |||
Python dictionary with request data. | |||
""" | |||
return json.loads(request) | |||
def _process_request(self, request): | |||
"""Accept serialized request from VSCode and write response. | |||
""" | |||
request = self._deserialize(request) | |||
lookup = request.get('lookup', '') | |||
if lookup == '': | |||
pass | |||
elif lookup == 'rename': | |||
changes = self._rename(request['file'], int( | |||
request['start']), request['name'], int(request['indent_size'])) | |||
return self._write_response(self._serialize(request['id'], changes)) | |||
elif lookup == 'extract_variable': | |||
changes = self._extractVariable(request['file'], int( | |||
request['start']), int(request['end']), request['name'], int(request['indent_size'])) | |||
return self._write_response(self._serialize(request['id'], changes)) | |||
elif lookup == 'extract_method': | |||
changes = self._extractMethod(request['file'], int( | |||
request['start']), int(request['end']), request['name'], int(request['indent_size'])) | |||
return self._write_response(self._serialize(request['id'], changes)) | |||
def _write_response(self, response): | |||
sys.stdout.write(response + '\n') | |||
sys.stdout.flush() | |||
def watch(self): | |||
self._write_response("STARTED") | |||
while True: | |||
try: | |||
self._process_request(self._input.readline()) | |||
except: | |||
exc_type, exc_value, exc_tb = sys.exc_info() | |||
tb_info = traceback.extract_tb(exc_tb) | |||
jsonMessage = {'error': True, 'message': str(exc_value), 'traceback': str(tb_info), 'type': str(exc_type)} | |||
sys.stderr.write(json.dumps(jsonMessage)) | |||
sys.stderr.flush() | |||
if __name__ == '__main__': | |||
RopeRefactoring().watch() |
@@ -1,12 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
import os | |||
import os.path | |||
import sys | |||
isort_path = os.path.join(os.path.dirname(__file__), 'lib', 'python') | |||
sys.path.insert(0, isort_path) | |||
import isort.main | |||
isort.main.main() |
@@ -1,95 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
import ast | |||
import json | |||
import sys | |||
class Visitor(ast.NodeVisitor): | |||
def __init__(self): | |||
self.symbols = {"classes": [], "methods": [], "functions": []} | |||
def visit_Module(self, node): | |||
self.visitChildren(node) | |||
def visitChildren(self, node, namespace=""): | |||
for child in node.body: | |||
if isinstance(child, ast.FunctionDef): | |||
self.visitDef(child, namespace) | |||
if isinstance(child, ast.ClassDef): | |||
self.visitClassDef(child, namespace) | |||
try: | |||
if isinstance(child, ast.AsyncFunctionDef): | |||
self.visitDef(child, namespace) | |||
except Exception: | |||
pass | |||
def visitDef(self, node, namespace=""): | |||
end_position = self.getEndPosition(node) | |||
symbol = "functions" if namespace == "" else "methods" | |||
self.symbols[symbol].append(self.getDataObject(node, namespace)) | |||
def visitClassDef(self, node, namespace=""): | |||
end_position = self.getEndPosition(node) | |||
self.symbols['classes'].append(self.getDataObject(node, namespace)) | |||
if len(namespace) > 0: | |||
namespace = "{0}::{1}".format(namespace, node.name) | |||
else: | |||
namespace = node.name | |||
self.visitChildren(node, namespace) | |||
def getDataObject(self, node, namespace=""): | |||
end_position = self.getEndPosition(node) | |||
return { | |||
"namespace": namespace, | |||
"name": node.name, | |||
"range": { | |||
"start": { | |||
"line": node.lineno - 1, | |||
"character": node.col_offset | |||
}, | |||
"end": { | |||
"line": end_position[0], | |||
"character": end_position[1] | |||
} | |||
} | |||
} | |||
def getEndPosition(self, node): | |||
if not hasattr(node, 'body') or len(node.body) == 0: | |||
return (node.lineno - 1, node.col_offset) | |||
return self.getEndPosition(node.body[-1]) | |||
def provide_symbols(source): | |||
"""Provides a list of all symbols in provided code. | |||
The list comprises of 3-item tuples that contain the starting line number, | |||
ending line number and whether the statement is a single line. | |||
""" | |||
tree = ast.parse(source) | |||
visitor = Visitor() | |||
visitor.visit(tree) | |||
sys.stdout.write(json.dumps(visitor.symbols)) | |||
sys.stdout.flush() | |||
if __name__ == "__main__": | |||
if len(sys.argv) == 3: | |||
contents = sys.argv[2] | |||
else: | |||
with open(sys.argv[1], "r") as source: | |||
contents = source.read() | |||
try: | |||
default_encoding = sys.getdefaultencoding() | |||
encoded_contents = contents.encode(default_encoding, 'surrogateescape') | |||
contents = encoded_contents.decode(default_encoding, 'replace') | |||
except (UnicodeError, LookupError): | |||
pass | |||
if isinstance(contents, bytes): | |||
contents = contents.decode('utf8') | |||
provide_symbols(contents) |
@@ -1,2 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. |
@@ -1,2 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. |
@@ -1,98 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
from __future__ import absolute_import | |||
import argparse | |||
import sys | |||
from . import pytest, report | |||
from .errors import UnsupportedToolError, UnsupportedCommandError | |||
TOOLS = { | |||
'pytest': { | |||
'_add_subparser': pytest.add_cli_subparser, | |||
'discover': pytest.discover, | |||
}, | |||
} | |||
REPORTERS = { | |||
'discover': report.report_discovered, | |||
} | |||
def parse_args( | |||
argv=sys.argv[1:], | |||
prog=sys.argv[0], | |||
): | |||
""" | |||
Return the subcommand & tool to run, along with its args. | |||
This defines the standard CLI for the different testing frameworks. | |||
""" | |||
parser = argparse.ArgumentParser( | |||
description='Run Python testing operations.', | |||
prog=prog, | |||
) | |||
cmdsubs = parser.add_subparsers(dest='cmd') | |||
# Add "run" and "debug" subcommands when ready. | |||
for cmdname in ['discover']: | |||
sub = cmdsubs.add_parser(cmdname) | |||
subsubs = sub.add_subparsers(dest='tool') | |||
for toolname in sorted(TOOLS): | |||
try: | |||
add_subparser = TOOLS[toolname]['_add_subparser'] | |||
except KeyError: | |||
continue | |||
subsub = add_subparser(cmdname, toolname, subsubs) | |||
if cmdname == 'discover': | |||
subsub.add_argument('--simple', action='store_true') | |||
subsub.add_argument('--no-hide-stdio', dest='hidestdio', | |||
action='store_false') | |||
subsub.add_argument('--pretty', action='store_true') | |||
# Parse the args! | |||
if '--' in argv: | |||
seppos = argv.index('--') | |||
toolargs = argv[seppos + 1:] | |||
argv = argv[:seppos] | |||
else: | |||
toolargs = [] | |||
args = parser.parse_args(argv) | |||
ns = vars(args) | |||
cmd = ns.pop('cmd') | |||
if not cmd: | |||
parser.error('missing command') | |||
tool = ns.pop('tool') | |||
if not tool: | |||
parser.error('missing tool') | |||
return tool, cmd, ns, toolargs | |||
def main(toolname, cmdname, subargs, toolargs, | |||
_tools=TOOLS, _reporters=REPORTERS): | |||
try: | |||
tool = _tools[toolname] | |||
except KeyError: | |||
raise UnsupportedToolError(toolname) | |||
try: | |||
run = tool[cmdname] | |||
report_result = _reporters[cmdname] | |||
except KeyError: | |||
raise UnsupportedCommandError(cmdname) | |||
parents, result = run(toolargs, **subargs) | |||
report_result(result, parents, | |||
**subargs | |||
) | |||
if __name__ == '__main__': | |||
tool, cmd, subargs, toolargs = parse_args() | |||
main(tool, cmd, subargs, toolargs) |
@@ -1,16 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
class UnsupportedToolError(ValueError): | |||
def __init__(self, tool): | |||
msg = 'unsupported tool {!r}'.format(tool) | |||
super(UnsupportedToolError, self).__init__(msg) | |||
self.tool = tool | |||
class UnsupportedCommandError(ValueError): | |||
def __init__(self, cmd): | |||
msg = 'unsupported cmd {!r}'.format(cmd) | |||
super(UnsupportedCommandError, self).__init__(msg) | |||
self.cmd = cmd |
@@ -1,114 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
from collections import namedtuple | |||
class TestPath(namedtuple('TestPath', 'root relfile func sub')): | |||
"""Where to find a single test.""" | |||
def __new__(cls, root, relfile, func, sub=None): | |||
self = super(TestPath, cls).__new__( | |||
cls, | |||
str(root) if root else None, | |||
str(relfile) if relfile else None, | |||
str(func) if func else None, | |||
[str(s) for s in sub] if sub else None, | |||
) | |||
return self | |||
def __init__(self, *args, **kwargs): | |||
if self.root is None: | |||
raise TypeError('missing id') | |||
if self.relfile is None: | |||
raise TypeError('missing kind') | |||
# self.func may be None (e.g. for doctests). | |||
# self.sub may be None. | |||
class ParentInfo(namedtuple('ParentInfo', 'id kind name root parentid')): | |||
KINDS = ('folder', 'file', 'suite', 'function', 'subtest') | |||
def __new__(cls, id, kind, name, root=None, parentid=None): | |||
self = super(ParentInfo, cls).__new__( | |||
cls, | |||
str(id) if id else None, | |||
str(kind) if kind else None, | |||
str(name) if name else None, | |||
str(root) if root else None, | |||
str(parentid) if parentid else None, | |||
) | |||
return self | |||
def __init__(self, *args, **kwargs): | |||
if self.id is None: | |||
raise TypeError('missing id') | |||
if self.kind is None: | |||
raise TypeError('missing kind') | |||
if self.kind not in self.KINDS: | |||
raise ValueError('unsupported kind {!r}'.format(self.kind)) | |||
if self.name is None: | |||
raise TypeError('missing name') | |||
if self.root is None: | |||
if self.parentid is not None or self.kind != 'folder': | |||
raise TypeError('missing root') | |||
elif self.parentid is None: | |||
raise TypeError('missing parentid') | |||
class TestInfo(namedtuple('TestInfo', 'id name path source markers parentid kind')): | |||
"""Info for a single test.""" | |||
MARKERS = ('skip', 'skip-if', 'expected-failure') | |||
KINDS = ('function', 'doctest') | |||
def __new__(cls, id, name, path, source, markers, parentid, kind='function'): | |||
self = super(TestInfo, cls).__new__( | |||
cls, | |||
str(id) if id else None, | |||
str(name) if name else None, | |||
path or None, | |||
str(source) if source else None, | |||
[str(marker) for marker in markers or ()], | |||
str(parentid) if parentid else None, | |||
str(kind) if kind else None, | |||
) | |||
return self | |||
def __init__(self, *args, **kwargs): | |||
if self.id is None: | |||
raise TypeError('missing id') | |||
if self.name is None: | |||
raise TypeError('missing name') | |||
if self.path is None: | |||
raise TypeError('missing path') | |||
if self.source is None: | |||
raise TypeError('missing source') | |||
else: | |||
srcfile, _, lineno = self.source.rpartition(':') | |||
if not srcfile or not lineno or int(lineno) < 0: | |||
raise ValueError('bad source {!r}'.format(self.source)) | |||
if self.markers: | |||
badmarkers = [m for m in self.markers if m not in self.MARKERS] | |||
if badmarkers: | |||
raise ValueError('unsupported markers {!r}'.format(badmarkers)) | |||
if self.parentid is None: | |||
raise TypeError('missing parentid') | |||
if self.kind is None: | |||
raise TypeError('missing kind') | |||
elif self.kind not in self.KINDS: | |||
raise ValueError('unsupported kind {!r}'.format(self.kind)) | |||
@property | |||
def root(self): | |||
return self.path.root | |||
@property | |||
def srcfile(self): | |||
return self.source.rpartition(':')[0] | |||
@property | |||
def lineno(self): | |||
return int(self.source.rpartition(':')[-1]) |
@@ -1,403 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
from __future__ import absolute_import | |||
import os.path | |||
import pytest | |||
from . import util | |||
from .errors import UnsupportedCommandError | |||
from .info import TestInfo, TestPath, ParentInfo | |||
def add_cli_subparser(cmd, name, parent): | |||
"""Add a new subparser to the given parent and add args to it.""" | |||
parser = parent.add_parser(name) | |||
if cmd == 'discover': | |||
# For now we don't have any tool-specific CLI options to add. | |||
pass | |||
else: | |||
raise UnsupportedCommandError(cmd) | |||
return parser | |||
def discover(pytestargs=None, hidestdio=False, | |||
_pytest_main=pytest.main, _plugin=None, **_ignored): | |||
"""Return the results of test discovery.""" | |||
if _plugin is None: | |||
_plugin = TestCollector() | |||
pytestargs = _adjust_pytest_args(pytestargs) | |||
# We use this helper rather than "-pno:terminal" due to possible | |||
# platform-dependent issues. | |||
with util.hide_stdio() if hidestdio else util.noop_cm(): | |||
ec = _pytest_main(pytestargs, [_plugin]) | |||
if ec != 0: | |||
raise Exception('pytest discovery failed (exit code {})'.format(ec)) | |||
if not _plugin._started: | |||
raise Exception('pytest discovery did not start') | |||
return ( | |||
_plugin._tests.parents, | |||
#[p._replace( | |||
# id=p.id.lstrip('.' + os.path.sep), | |||
# parentid=p.parentid.lstrip('.' + os.path.sep), | |||
# ) | |||
# for p in _plugin._tests.parents], | |||
list(_plugin._tests), | |||
) | |||
def _adjust_pytest_args(pytestargs): | |||
pytestargs = list(pytestargs) if pytestargs else [] | |||
# Duplicate entries should be okay. | |||
pytestargs.insert(0, '--collect-only') | |||
# TODO: pull in code from: | |||
# src/client/unittests/pytest/services/discoveryService.ts | |||
# src/client/unittests/pytest/services/argsService.ts | |||
return pytestargs | |||
class TestCollector(object): | |||
"""This is a pytest plugin that collects the discovered tests.""" | |||
NORMCASE = staticmethod(os.path.normcase) | |||
PATHSEP = os.path.sep | |||
def __init__(self, tests=None): | |||
if tests is None: | |||
tests = DiscoveredTests() | |||
self._tests = tests | |||
self._started = False | |||
# Relevant plugin hooks: | |||
# https://docs.pytest.org/en/latest/reference.html#collection-hooks | |||
def pytest_collection_modifyitems(self, session, config, items): | |||
self._started = True | |||
self._tests.reset() | |||
for item in items: | |||
test, suiteids = _parse_item(item, self.NORMCASE, self.PATHSEP) | |||
self._tests.add_test(test, suiteids) | |||
# This hook is not specified in the docs, so we also provide | |||
# the "modifyitems" hook just in case. | |||
def pytest_collection_finish(self, session): | |||
self._started = True | |||
try: | |||
items = session.items | |||
except AttributeError: | |||
# TODO: Is there an alternative? | |||
return | |||
self._tests.reset() | |||
for item in items: | |||
test, suiteids = _parse_item(item, self.NORMCASE, self.PATHSEP) | |||
self._tests.add_test(test, suiteids) | |||
class DiscoveredTests(object): | |||
def __init__(self): | |||
self.reset() | |||
def __len__(self): | |||
return len(self._tests) | |||
def __getitem__(self, index): | |||
return self._tests[index] | |||
@property | |||
def parents(self): | |||
return sorted(self._parents.values(), key=lambda v: (v.root or v.name, v.id)) | |||
def reset(self): | |||
self._parents = {} | |||
self._tests = [] | |||
def add_test(self, test, suiteids): | |||
parentid = self._ensure_parent(test.path, test.parentid, suiteids) | |||
test = test._replace(parentid=parentid) | |||
if not test.id.startswith('.' + os.path.sep): | |||
test = test._replace(id=os.path.join('.', test.id)) | |||
self._tests.append(test) | |||
def _ensure_parent(self, path, parentid, suiteids): | |||
if not parentid.startswith('.' + os.path.sep): | |||
parentid = os.path.join('.', parentid) | |||
fileid = self._ensure_file(path.root, path.relfile) | |||
rootdir = path.root | |||
if not path.func: | |||
return parentid | |||
fullsuite, _, funcname = path.func.rpartition('.') | |||
suiteid = self._ensure_suites(fullsuite, rootdir, fileid, suiteids) | |||
parent = suiteid if suiteid else fileid | |||
if path.sub: | |||
if (rootdir, parentid) not in self._parents: | |||
funcinfo = ParentInfo(parentid, 'function', funcname, | |||
rootdir, parent) | |||
self._parents[(rootdir, parentid)] = funcinfo | |||
elif parent != parentid: | |||
# TODO: What to do? | |||
raise NotImplementedError | |||
return parentid | |||
def _ensure_file(self, rootdir, relfile): | |||
if (rootdir, '.') not in self._parents: | |||
self._parents[(rootdir, '.')] = ParentInfo('.', 'folder', rootdir) | |||
if relfile.startswith('.' + os.path.sep): | |||
fileid = relfile | |||
else: | |||
fileid = relfile = os.path.join('.', relfile) | |||
if (rootdir, fileid) not in self._parents: | |||
folderid, filebase = os.path.split(fileid) | |||
fileinfo = ParentInfo(fileid, 'file', filebase, rootdir, folderid) | |||
self._parents[(rootdir, fileid)] = fileinfo | |||
while folderid != '.' and (rootdir, folderid) not in self._parents: | |||
parentid, name = os.path.split(folderid) | |||
folderinfo = ParentInfo(folderid, 'folder', name, rootdir, parentid) | |||
self._parents[(rootdir, folderid)] = folderinfo | |||
folderid = parentid | |||
return relfile | |||
def _ensure_suites(self, fullsuite, rootdir, fileid, suiteids): | |||
if not fullsuite: | |||
if suiteids: | |||
# TODO: What to do? | |||
raise NotImplementedError | |||
return None | |||
if len(suiteids) != fullsuite.count('.') + 1: | |||
# TODO: What to do? | |||
raise NotImplementedError | |||
suiteid = suiteids.pop() | |||
if not suiteid.startswith('.' + os.path.sep): | |||
suiteid = os.path.join('.', suiteid) | |||
final = suiteid | |||
while '.' in fullsuite and (rootdir, suiteid) not in self._parents: | |||
parentid = suiteids.pop() | |||
if not parentid.startswith('.' + os.path.sep): | |||
parentid = os.path.join('.', parentid) | |||
fullsuite, _, name = fullsuite.rpartition('.') | |||
suiteinfo = ParentInfo(suiteid, 'suite', name, rootdir, parentid) | |||
self._parents[(rootdir, suiteid)] = suiteinfo | |||
suiteid = parentid | |||
else: | |||
name = fullsuite | |||
suiteinfo = ParentInfo(suiteid, 'suite', name, rootdir, fileid) | |||
if (rootdir, suiteid) not in self._parents: | |||
self._parents[(rootdir, suiteid)] = suiteinfo | |||
return final | |||
def _parse_item(item, _normcase, _pathsep): | |||
""" | |||
(pytest.Collector) | |||
pytest.Session | |||
pytest.Package | |||
pytest.Module | |||
pytest.Class | |||
(pytest.File) | |||
(pytest.Item) | |||
pytest.Function | |||
""" | |||
#_debug_item(item, showsummary=True) | |||
kind, _ = _get_item_kind(item) | |||
# Figure out the func, suites, and subs. | |||
(fileid, suiteids, suites, funcid, basename, parameterized | |||
) = _parse_node_id(item.nodeid, kind) | |||
if kind == 'function': | |||
funcname = basename | |||
if funcid and item.function.__name__ != funcname: | |||
# TODO: What to do? | |||
raise NotImplementedError | |||
if suites: | |||
testfunc = '.'.join(suites) + '.' + funcname | |||
else: | |||
testfunc = funcname | |||
elif kind == 'doctest': | |||
testfunc = None | |||
funcname = None | |||
# Figure out the file. | |||
fspath = str(item.fspath) | |||
if not fspath.endswith(_pathsep + fileid): | |||
raise NotImplementedError | |||
filename = fspath[-len(fileid):] | |||
testroot = str(item.fspath)[:-len(fileid)].rstrip(_pathsep) | |||
if _pathsep in filename: | |||
relfile = filename | |||
else: | |||
relfile = '.' + _pathsep + filename | |||
srcfile, lineno, fullname = item.location | |||
if srcfile != fileid: | |||
# pytest supports discovery of tests imported from other | |||
# modules. This is reflected by a different filename | |||
# in item.location. | |||
if _normcase(fileid) == _normcase(srcfile): | |||
srcfile = fileid | |||
else: | |||
srcfile = relfile | |||
location = '{}:{}'.format(srcfile, lineno) | |||
if kind == 'function': | |||
if testfunc and fullname != testfunc + parameterized: | |||
print(fullname, testfunc) | |||
# TODO: What to do? | |||
raise NotImplementedError | |||
elif kind == 'doctest': | |||
if testfunc and fullname != testfunc + parameterized: | |||
print(fullname, testfunc) | |||
# TODO: What to do? | |||
raise NotImplementedError | |||
# Sort out the parent. | |||
if parameterized: | |||
parentid = funcid | |||
elif suites: | |||
parentid = suiteids[-1] | |||
else: | |||
parentid = fileid | |||
# Sort out markers. | |||
# See: https://docs.pytest.org/en/latest/reference.html#marks | |||
markers = set() | |||
for marker in item.own_markers: | |||
if marker.name == 'parameterize': | |||
# We've already covered these. | |||
continue | |||
elif marker.name == 'skip': | |||
markers.add('skip') | |||
elif marker.name == 'skipif': | |||
markers.add('skip-if') | |||
elif marker.name == 'xfail': | |||
markers.add('expected-failure') | |||
# TODO: Support other markers? | |||
test = TestInfo( | |||
id=item.nodeid, | |||
name=item.name, | |||
path=TestPath( | |||
root=testroot, | |||
relfile=relfile, | |||
func=testfunc, | |||
sub=[parameterized] if parameterized else None, | |||
), | |||
source=location, | |||
markers=sorted(markers) if markers else None, | |||
parentid=parentid, | |||
) | |||
return test, suiteids | |||
def _parse_node_id(nodeid, kind='function'): | |||
if kind == 'doctest': | |||
try: | |||
parentid, name = nodeid.split('::') | |||
except ValueError: | |||
# TODO: Unexpected! What to do? | |||
raise NotImplementedError | |||
funcid = None | |||
parameterized = '' | |||
else: | |||
parameterized = '' | |||
if nodeid.endswith(']'): | |||
funcid, sep, parameterized = nodeid.partition('[') | |||
if not sep: | |||
# TODO: Unexpected! What to do? | |||
raise NotImplementedError | |||
parameterized = sep + parameterized | |||
else: | |||
funcid = nodeid | |||
parentid, _, name = funcid.rpartition('::') | |||
if not name: | |||
# TODO: What to do? We expect at least a filename and a function | |||
raise NotImplementedError | |||
suites = [] | |||
suiteids = [] | |||
while '::' in parentid: | |||
suiteids.insert(0, parentid) | |||
parentid, _, suitename = parentid.rpartition('::') | |||
suites.insert(0, suitename) | |||
fileid = parentid | |||
return fileid, suiteids, suites, funcid, name, parameterized | |||
def _get_item_kind(item): | |||
"""Return (kind, isunittest) for the given item.""" | |||
try: | |||
itemtype = item.kind | |||
except AttributeError: | |||
itemtype = item.__class__.__name__ | |||
if itemtype == 'DoctestItem': | |||
return 'doctest', False | |||
elif itemtype == 'Function': | |||
return 'function', False | |||
elif itemtype == 'TestCaseFunction': | |||
return 'function', True | |||
elif item.hasattr('function'): | |||
return 'function', False | |||
else: | |||
return None, False | |||
############################# | |||
# useful for debugging | |||
def _debug_item(item, showsummary=False): | |||
item._debugging = True | |||
try: | |||
# TODO: Make a PytestTest class to wrap the item? | |||
summary = { | |||
'id': item.nodeid, | |||
'kind': _get_item_kind(item), | |||
'class': item.__class__.__name__, | |||
'name': item.name, | |||
'fspath': item.fspath, | |||
'location': item.location, | |||
'func': getattr(item, 'function', None), | |||
'markers': item.own_markers, | |||
#'markers': list(item.iter_markers()), | |||
'props': item.user_properties, | |||
'attrnames': dir(item), | |||
} | |||
finally: | |||
item._debugging = False | |||
if showsummary: | |||
print(item.nodeid) | |||
for key in ('kind', 'class', 'name', 'fspath', 'location', 'func', | |||
'markers', 'props'): | |||
print(' {:12} {}'.format(key, summary[key])) | |||
print() | |||
return summary | |||
def _group_attr_names(attrnames): | |||
grouped = { | |||
'dunder': [n for n in attrnames | |||
if n.startswith('__') and n.endswith('__')], | |||
'private': [n for n in attrnames if n.startswith('_')], | |||
'constants': [n for n in attrnames if n.isupper()], | |||
'classes': [n for n in attrnames | |||
if n == n.capitalize() and not n.isupper()], | |||
'vars': [n for n in attrnames if n.islower()], | |||
} | |||
grouped['other'] = [n for n in attrnames | |||
if n not in grouped['dunder'] | |||
and n not in grouped['private'] | |||
and n not in grouped['constants'] | |||
and n not in grouped['classes'] | |||
and n not in grouped['vars'] | |||
] | |||
return grouped |
@@ -1,74 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
from __future__ import print_function | |||
import json | |||
def report_discovered(tests, parents, pretty=False, simple=False, | |||
_send=print, **_ignored): | |||
"""Serialize the discovered tests and write to stdout.""" | |||
if simple: | |||
data = [{ | |||
'id': test.id, | |||
'name': test.name, | |||
'testroot': test.path.root, | |||
'relfile': test.path.relfile, | |||
'lineno': test.lineno, | |||
'testfunc': test.path.func, | |||
'subtest': test.path.sub or None, | |||
'markers': test.markers or [], | |||
} for test in tests] | |||
else: | |||
byroot = {} | |||
for parent in parents: | |||
rootdir = parent.name if parent.root is None else parent.root | |||
try: | |||
root = byroot[rootdir] | |||
except KeyError: | |||
root = byroot[rootdir] = { | |||
'id': rootdir, | |||
'parents': [], | |||
'tests': [], | |||
} | |||
if not parent.root: | |||
root['id'] = parent.id | |||
continue | |||
root['parents'].append({ | |||
'id': parent.id, | |||
'kind': parent.kind, | |||
'name': parent.name, | |||
'parentid': parent.parentid, | |||
}) | |||
for test in tests: | |||
# We are guaranteed that the parent was added. | |||
root = byroot[test.path.root] | |||
testdata = { | |||
'id': test.id, | |||
'name': test.name, | |||
# TODO: Add a "kind" field | |||
# (e.g. "unittest", "function", "doctest") | |||
'source': test.source, | |||
'markers': test.markers or [], | |||
'parentid': test.parentid, | |||
} | |||
root['tests'].append(testdata) | |||
data = [{ | |||
'rootid': byroot[root]['id'], | |||
'root': root, | |||
'parents': byroot[root]['parents'], | |||
'tests': byroot[root]['tests'], | |||
} for root in sorted(byroot)] | |||
kwargs = {} | |||
if pretty: | |||
# human-formatted | |||
kwargs = dict( | |||
sort_keys=True, | |||
indent=4, | |||
separators=(',', ': '), | |||
) | |||
serialized = json.dumps(data, **kwargs) | |||
_send(serialized) |
@@ -1,33 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
import contextlib | |||
try: | |||
from io import StringIO | |||
except ImportError: | |||
from StringIO import StringIO # 2.7 | |||
import sys | |||
@contextlib.contextmanager | |||
def noop_cm(): | |||
yield | |||
@contextlib.contextmanager | |||
def hide_stdio(): | |||
"""Swallow stdout and stderr.""" | |||
ignored = IgnoredIO() | |||
sys.stdout = ignored | |||
sys.stderr = ignored | |||
try: | |||
yield | |||
finally: | |||
sys.stdout = sys.__stdout__ | |||
sys.stderr = sys.__stderr__ | |||
class IgnoredIO(StringIO): | |||
"""A noop "file".""" | |||
def write(self, msg): | |||
pass |
@@ -1,16 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
# Replace the "." entry. | |||
import os.path | |||
import sys | |||
sys.path[0] = os.path.dirname( | |||
os.path.dirname( | |||
os.path.abspath(__file__))) | |||
from testing_tools.adapter.__main__ import parse_args, main | |||
if __name__ == '__main__': | |||
tool, cmd, subargs, toolargs = parse_args() | |||
main(tool, cmd, subargs, toolargs) |
@@ -1,64 +0,0 @@ | |||
# Copyright (c) Microsoft Corporation. All rights reserved. | |||
# Licensed under the MIT License. | |||
import os | |||
import sys | |||
def parse_argv(): | |||
"""Parses arguments for use with the test launcher. | |||
Arguments are: | |||
1. Working directory. | |||
2. Test runner, `pytest` or `nose` | |||
3. Rest of the arguments are passed into the test runner. | |||
""" | |||
return (sys.argv[1], sys.argv[2], sys.argv[3:]) | |||
def exclude_current_file_from_debugger(): | |||
# Load the debugger package | |||
try: | |||
import ptvsd | |||
import ptvsd.debugger as vspd | |||
vspd.DONT_DEBUG.append(os.path.normcase(__file__)) | |||
except: | |||
traceback.print_exc() | |||
print(''' | |||
Internal error detected. Please copy the above traceback and report at | |||
https://github.com/Microsoft/vscode-python/issues/new | |||
Press Enter to close. . .''') | |||
try: | |||
raw_input() | |||
except NameError: | |||
input() | |||
sys.exit(1) | |||
def run(cwd, testRunner, args): | |||
"""Runs the test | |||
cwd -- the current directory to be set | |||
testRunner -- test runner to be used `pytest` or `nose` | |||
args -- arguments passed into the test runner | |||
""" | |||
sys.path[0] = os.getcwd() | |||
os.chdir(cwd) | |||
try: | |||
if testRunner == 'pytest': | |||
import pytest | |||
pytest.main(args) | |||
else: | |||
import nose | |||
nose.run(argv=args) | |||
sys.exit(0) | |||
finally: | |||
pass | |||
if __name__ == '__main__': | |||
exclude_current_file_from_debugger() | |||
cwd, testRunner, args = parse_argv() | |||
run(cwd, testRunner, args) |
@@ -1,347 +0,0 @@ | |||
# Python Tools for Visual Studio | |||
# Copyright(c) Microsoft Corporation | |||
# All rights reserved. | |||
# | |||
# Licensed under the Apache License, Version 2.0 (the License); you may not use | |||
# this file except in compliance with the License. You may obtain a copy of the | |||
# License at http://www.apache.org/licenses/LICENSE-2.0 | |||
# | |||
# THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS | |||
# OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY | |||
# IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | |||
# MERCHANTABLITY OR NON-INFRINGEMENT. | |||
# | |||
# See the Apache Version 2.0 License for specific language governing | |||
# permissions and limitations under the License. | |||
__author__ = "Microsoft Corporation <ptvshelp@microsoft.com>" | |||
__version__ = "3.0.0.0" | |||
import os | |||
import sys | |||
import json | |||
import unittest | |||
import socket | |||
import traceback | |||
from types import CodeType, FunctionType | |||
import signal | |||
try: | |||
import thread | |||
except: | |||
import _thread as thread | |||
class _TestOutput(object): | |||
"""file like object which redirects output to the repl window.""" | |||
errors = 'strict' | |||
def __init__(self, old_out, is_stdout): | |||
self.is_stdout = is_stdout | |||
self.old_out = old_out | |||
if sys.version >= '3.' and hasattr(old_out, 'buffer'): | |||
self.buffer = _TestOutputBuffer(old_out.buffer, is_stdout) | |||
def flush(self): | |||
if self.old_out: | |||
self.old_out.flush() | |||
def writelines(self, lines): | |||
for line in lines: | |||
self.write(line) | |||
@property | |||
def encoding(self): | |||
return 'utf8' | |||
def write(self, value): | |||
_channel.send_event('stdout' if self.is_stdout else 'stderr', content=value) | |||
if self.old_out: | |||
self.old_out.write(value) | |||
# flush immediately, else things go wonky and out of order | |||
self.flush() | |||
def isatty(self): | |||
return True | |||
def next(self): | |||
pass | |||
@property | |||
def name(self): | |||
if self.is_stdout: | |||
return "<stdout>" | |||
else: | |||
return "<stderr>" | |||
def __getattr__(self, name): | |||
return getattr(self.old_out, name) | |||
class _TestOutputBuffer(object): | |||
def __init__(self, old_buffer, is_stdout): | |||
self.buffer = old_buffer | |||
self.is_stdout = is_stdout | |||
def write(self, data): | |||
_channel.send_event('stdout' if self.is_stdout else 'stderr', content=data) | |||
self.buffer.write(data) | |||
def flush(self): | |||
self.buffer.flush() | |||
def truncate(self, pos = None): | |||
return self.buffer.truncate(pos) | |||
def tell(self): | |||
return self.buffer.tell() | |||
def seek(self, pos, whence = 0): | |||
return self.buffer.seek(pos, whence) | |||
class _IpcChannel(object): | |||
def __init__(self, socket, callback): | |||
self.socket = socket | |||
self.seq = 0 | |||
self.callback = callback | |||
self.lock = thread.allocate_lock() | |||
self._closed = False | |||
# start the testing reader thread loop | |||
self.test_thread_id = thread.start_new_thread(self.readSocket, ()) | |||
def close(self): | |||
self._closed = True | |||
def readSocket(self): | |||
try: | |||
data = self.socket.recv(1024) | |||
self.callback() | |||
except OSError: | |||
if not self._closed: | |||
raise | |||
def receive(self): | |||
pass | |||
def send_event(self, name, **args): | |||
with self.lock: | |||
body = {'type': 'event', 'seq': self.seq, 'event':name, 'body':args} | |||
self.seq += 1 | |||
content = json.dumps(body).encode('utf8') | |||
headers = ('Content-Length: %d\n\n' % (len(content), )).encode('utf8') | |||
self.socket.send(headers) | |||
self.socket.send(content) | |||
_channel = None | |||
class VsTestResult(unittest.TextTestResult): | |||
def startTest(self, test): | |||
super(VsTestResult, self).startTest(test) | |||
if _channel is not None: | |||
_channel.send_event( | |||
name='start', | |||
test = test.id() | |||
) | |||
def addError(self, test, err): | |||
super(VsTestResult, self).addError(test, err) | |||
self.sendResult(test, 'error', err) | |||
def addFailure(self, test, err): | |||
super(VsTestResult, self).addFailure(test, err) | |||
self.sendResult(test, 'failed', err) | |||
def addSuccess(self, test): | |||
super(VsTestResult, self).addSuccess(test) | |||
self.sendResult(test, 'passed') | |||
def addSkip(self, test, reason): | |||
super(VsTestResult, self).addSkip(test, reason) | |||
self.sendResult(test, 'skipped') | |||
def addExpectedFailure(self, test, err): | |||
super(VsTestResult, self).addExpectedFailure(test, err) | |||
self.sendResult(test, 'failed', err) | |||
def addUnexpectedSuccess(self, test): | |||
super(VsTestResult, self).addUnexpectedSuccess(test) | |||
self.sendResult(test, 'passed') | |||
def sendResult(self, test, outcome, trace = None): | |||
if _channel is not None: | |||
tb = None | |||
message = None | |||
if trace is not None: | |||
traceback.print_exc() | |||
formatted = traceback.format_exception(*trace) | |||
# Remove the 'Traceback (most recent call last)' | |||
formatted = formatted[1:] | |||
tb = ''.join(formatted) | |||
message = str(trace[1]) | |||
_channel.send_event( | |||
name='result', | |||
outcome=outcome, | |||
traceback = tb, | |||
message = message, | |||
test = test.id() | |||
) | |||
def stopTests(): | |||
try: | |||
os.kill(os.getpid(), signal.SIGUSR1) | |||
except: | |||
try: | |||
os.kill(os.getpid(), signal.SIGTERM) | |||
except: | |||
pass | |||
class ExitCommand(Exception): | |||
pass | |||
def signal_handler(signal, frame): | |||
raise ExitCommand() | |||
def main(): | |||
import os | |||
import sys | |||
import unittest | |||
from optparse import OptionParser | |||
global _channel | |||
parser = OptionParser(prog = 'visualstudio_py_testlauncher', usage = 'Usage: %prog [<option>] <test names>... ') | |||
parser.add_option('--debug', action='store_true', help='Whether debugging the unit tests') | |||
parser.add_option('-x', '--mixed-mode', action='store_true', help='wait for mixed-mode debugger to attach') | |||
parser.add_option('-t', '--test', type='str', dest='tests', action='append', help='specifies a test to run') | |||
parser.add_option('--testFile', type='str', help='Fully qualitified path to file name') | |||
parser.add_option('-c', '--coverage', type='str', help='enable code coverage and specify filename') | |||
parser.add_option('-r', '--result-port', type='int', help='connect to port on localhost and send test results') | |||
parser.add_option('--us', type='str', help='Directory to start discovery') | |||
parser.add_option('--up', type='str', help='Pattern to match test files (''test*.py'' default)') | |||
parser.add_option('--ut', type='str', help='Top level directory of project (default to start directory)') | |||
parser.add_option('--uvInt', '--verboseInt', type='int', help='Verbose output (0 none, 1 (no -v) simple, 2 (-v) full)') | |||
parser.add_option('--uf', '--failfast', type='str', help='Stop on first failure') | |||
parser.add_option('--uc', '--catch', type='str', help='Catch control-C and display results') | |||
(opts, _) = parser.parse_args() | |||
if opts.debug: | |||
from ptvsd.visualstudio_py_debugger import DONT_DEBUG, DEBUG_ENTRYPOINTS, get_code | |||
sys.path[0] = os.getcwd() | |||
if opts.result_port: | |||
try: | |||
signal.signal(signal.SIGUSR1, signal_handler) | |||
except: | |||
try: | |||
signal.signal(signal.SIGTERM, signal_handler) | |||
except: | |||
pass | |||
_channel = _IpcChannel(socket.create_connection(('127.0.0.1', opts.result_port)), stopTests) | |||
sys.stdout = _TestOutput(sys.stdout, is_stdout = True) | |||
sys.stderr = _TestOutput(sys.stderr, is_stdout = False) | |||
if opts.debug: | |||
DONT_DEBUG.append(os.path.normcase(__file__)) | |||
DEBUG_ENTRYPOINTS.add(get_code(main)) | |||
pass | |||
elif opts.mixed_mode: | |||
# For mixed-mode attach, there's no ptvsd and hence no wait_for_attach(), | |||
# so we have to use Win32 API in a loop to do the same thing. | |||
from time import sleep | |||
from ctypes import windll, c_char | |||
while True: | |||
if windll.kernel32.IsDebuggerPresent() != 0: | |||
break | |||
sleep(0.1) | |||
try: | |||
debugger_helper = windll['Microsoft.PythonTools.Debugger.Helper.x86.dll'] | |||
except WindowsError: | |||
debugger_helper = windll['Microsoft.PythonTools.Debugger.Helper.x64.dll'] | |||
isTracing = c_char.in_dll(debugger_helper, "isTracing") | |||
while True: | |||
if isTracing.value != 0: | |||
break | |||
sleep(0.1) | |||
cov = None | |||
try: | |||
if opts.coverage: | |||
try: | |||
import coverage | |||
cov = coverage.coverage(opts.coverage) | |||
cov.load() | |||
cov.start() | |||
except: | |||
pass | |||
if opts.tests is None and opts.testFile is None: | |||
if opts.us is None: | |||
opts.us = '.' | |||
if opts.up is None: | |||
opts.up = 'test*.py' | |||
tests = unittest.defaultTestLoader.discover(opts.us, opts.up) | |||
else: | |||
# loadTestsFromNames doesn't work well (with duplicate file names or class names) | |||
# Easier approach is find the test suite and use that for running | |||
loader = unittest.TestLoader() | |||
# opts.us will be passed in | |||
suites = loader.discover(opts.us, pattern=os.path.basename(opts.testFile)) | |||
suite = None | |||
tests = None | |||
if opts.tests is None: | |||
# Run everything in the test file | |||
tests = suites | |||
else: | |||
# Run a specific test class or test method | |||
for test_suite in suites._tests: | |||
for cls in test_suite._tests: | |||
try: | |||
for m in cls._tests: | |||
testId = m.id() | |||
if testId.startswith(opts.tests[0]): | |||
suite = cls | |||
if testId == opts.tests[0]: | |||
tests = unittest.TestSuite([m]) | |||
break | |||
except Exception as err: | |||
errorMessage = traceback.format_exception() | |||
pass | |||
if tests is None: | |||
tests = suite | |||
if tests is None and suite is None: | |||
_channel.send_event( | |||
name='error', | |||
outcome='', | |||
traceback = '', | |||
message = 'Failed to identify the test', | |||
test = '' | |||
) | |||
if opts.uvInt is None: | |||
opts.uvInt = 0 | |||
if opts.uf is not None: | |||
runner = unittest.TextTestRunner(verbosity=opts.uvInt, resultclass=VsTestResult, failfast=True) | |||
else: | |||
runner = unittest.TextTestRunner(verbosity=opts.uvInt, resultclass=VsTestResult) | |||
result = runner.run(tests) | |||
if _channel is not None: | |||
_channel.close() | |||
sys.exit(not result.wasSuccessful()) | |||
finally: | |||
if cov is not None: | |||
cov.stop() | |||
cov.save() | |||
cov.xml_report(outfile = opts.coverage + '.xml', omit=__file__) | |||
if _channel is not None: | |||
_channel.send_event( | |||
name='done' | |||
) | |||
_channel.socket.close() | |||
# prevent generation of the error 'Error in sys.exitfunc:' | |||
try: | |||
sys.stdout.close() | |||
except: | |||
pass | |||
try: | |||
sys.stderr.close() | |||
except: | |||
pass | |||
if __name__ == '__main__': | |||
main() |
@@ -1,23 +0,0 @@ | |||
--recurse=yes | |||
--tag-relative=yes | |||
--exclude=.git | |||
--exclude=log | |||
--exclude=tmp | |||
--exclude=doc | |||
--exclude=deps | |||
--exclude=node_modules | |||
--exclude=.vscode | |||
--exclude=public/assets | |||
--exclude=*.git* | |||
--exclude=*.pyc | |||
--exclude=*.pyo | |||
--exclude=.DS_Store | |||
--exclude=**/*.jar | |||
--exclude=**/*.class | |||
--exclude=**/.idea/ | |||
--exclude=build | |||
--exclude=Builds | |||
--exclude=doc | |||
--fields=Knz | |||
--extras=+f | |||
--append=no |
@@ -1,53 +0,0 @@ | |||
{ | |||
"title": "conda environment file", | |||
"description": "Support for conda's enviroment.yml files (e.g. `conda env export > environment.yml`)", | |||
"id": "https://raw.githubusercontent.com/Microsoft/vscode-python/master/schemas/conda-environment.json", | |||
"$schema": "http://json-schema.org/draft-04/schema#", | |||
"definitions": { | |||
"channel": { | |||
"type": "string" | |||
}, | |||
"package": { | |||
"type": "string" | |||
}, | |||
"path": { | |||
"type": "string" | |||
} | |||
}, | |||
"properties": { | |||
"name": { | |||
"type": "string" | |||
}, | |||
"channels": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/channel" | |||
} | |||
}, | |||
"dependencies": { | |||
"type": "array", | |||
"items": { | |||
"anyOf": [ | |||
{ | |||
"$ref": "#/definitions/package" | |||
}, | |||
{ | |||
"type": "object", | |||
"properties": { | |||
"pip": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/package" | |||
} | |||
} | |||
}, | |||
"required": [ "pip" ] | |||
} | |||
] | |||
} | |||
}, | |||
"prefix": { | |||
"$ref": "#/definitions/path" | |||
} | |||
} | |||
} |
@@ -1,405 +0,0 @@ | |||
{ | |||
"title": "conda metadata build recipe", | |||
"description": "conda's meta.yaml file; https://conda.io/docs/user-guide/tasks/build-packages/define-metadata.html", | |||
"id": "https://raw.githubusercontent.com/Microsoft/vscode-python/master/schemas/conda-meta.json", | |||
"$schema": "http://json-schema.org/draft-04/schema#", | |||
"definitions": { | |||
"boolean": { | |||
"anyOf": [ | |||
{ | |||
"type": "boolean" | |||
}, | |||
{ | |||
"type": "string" | |||
} | |||
] | |||
}, | |||
"integer": { | |||
"anyOf": [ | |||
{ | |||
"type": "integer" | |||
}, | |||
{ | |||
"type": "string" | |||
} | |||
] | |||
}, | |||
"feature": { | |||
"type": "string" | |||
}, | |||
"package": { | |||
"type": "string" | |||
}, | |||
"path": { | |||
"type": "string" | |||
}, | |||
"uri": { | |||
"type": "string" | |||
}, | |||
"requirement": { | |||
"type": "object", | |||
"properties": { | |||
"build": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/package" | |||
} | |||
}, | |||
"host": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/package" | |||
} | |||
}, | |||
"run": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/package" | |||
} | |||
} | |||
} | |||
}, | |||
"source": { | |||
"type": "object", | |||
"properties": { | |||
"url": { | |||
"$ref": "#/definitions/uri" | |||
}, | |||
"md5": { | |||
"type": "string", | |||
"pattern": "^[0-9A-Fa-f]{32}$" | |||
}, | |||
"sha1": { | |||
"type": "string", | |||
"pattern": "^[0-9A-Fa-f]{40}$" | |||
}, | |||
"sha256": { | |||
"type": "string", | |||
"pattern": "^[0-9A-Fa-f]{64}$" | |||
}, | |||
"git_url": { | |||
"$ref": "#/definitions/uri" | |||
}, | |||
"git_rev": { | |||
"type": "string" | |||
}, | |||
"hg_url": { | |||
"$ref": "#/definitions/uri" | |||
}, | |||
"hg_tag": { | |||
"type": "string" | |||
}, | |||
"svn_url": { | |||
"$ref": "#/definitions/uri" | |||
}, | |||
"svn_rev": { | |||
"type": "string" | |||
}, | |||
"svn_ignore_externals": { | |||
"$ref": "#/definitions/boolean" | |||
}, | |||
"path": { | |||
"$ref": "#/definitions/path" | |||
}, | |||
"patches": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/path" | |||
} | |||
}, | |||
"folder": { | |||
"$ref": "#/definitions/path" | |||
}, | |||
"fn": { | |||
"type": "string" | |||
} | |||
} | |||
}, | |||
"test": { | |||
"type": "object", | |||
"properties": { | |||
"files": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/path" | |||
} | |||
}, | |||
"source_files": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/path" | |||
} | |||
}, | |||
"requires": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/package" | |||
} | |||
}, | |||
"commands": { | |||
"type": "array", | |||
"items": { | |||
"type": "string" | |||
} | |||
}, | |||
"imports": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/package" | |||
} | |||
}, | |||
"script": { | |||
"type": "string" | |||
} | |||
} | |||
} | |||
}, | |||
"properties": { | |||
"package": { | |||
"type": "object", | |||
"properties": { | |||
"name": { | |||
"$ref": "#/definitions/package" | |||
}, | |||
"version": { | |||
"type": "string" | |||
} | |||
} | |||
}, | |||
"source": { | |||
"oneOf": [ | |||
{ | |||
"$ref": "#/definitions/source" | |||
}, | |||
{ | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/source" | |||
} | |||
} | |||
] | |||
}, | |||
"build": { | |||
"type": "object", | |||
"properties": { | |||
"number": { | |||
"$ref": "#/definitions/integer" | |||
}, | |||
"string": { | |||
"type": "string" | |||
}, | |||
"entry_points": { | |||
"type": "array", | |||
"items": { | |||
"type": "string" | |||
} | |||
}, | |||
"osx_is_app": { | |||
"$ref": "#/definitions/boolean" | |||
}, | |||
"features": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/feature" | |||
} | |||
}, | |||
"track_features": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/feature" | |||
} | |||
}, | |||
"preserve_egg_dir": { | |||
"$ref": "#/definitions/boolean" | |||
}, | |||
"skip_compile_pyc": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/path" | |||
} | |||
}, | |||
"no_link": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/path" | |||
} | |||
}, | |||
"script": { | |||
"type": "string" | |||
}, | |||
"rpaths": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/path" | |||
} | |||
}, | |||
"always_include_files": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/path" | |||
} | |||
}, | |||
"binary_relocation": { | |||
"$ref": "#/definitions/boolean" | |||
}, | |||
"detect_binary_files_with_prefix": { | |||
"$ref": "#/definitions/boolean" | |||
}, | |||
"binary_has_prefix_files": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/path" | |||
} | |||
}, | |||
"has_prefix_files": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/path" | |||
} | |||
}, | |||
"ignore_prefix_files": { | |||
"oneOf": [ | |||
{ | |||
"$ref": "#/definitions/boolean" | |||
}, | |||
{ | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/path" | |||
} | |||
} | |||
] | |||
}, | |||
"skip": { | |||
"$ref": "#/definitions/boolean" | |||
}, | |||
"noarch": { | |||
"type": "string" | |||
}, | |||
"noarch_python": { | |||
"$ref": "#/definitions/boolean" | |||
}, | |||
"include_recipe": { | |||
"$ref": "#/definitions/boolean" | |||
}, | |||
"script_env": { | |||
"type": "array", | |||
"items": { | |||
"type": "string" | |||
} | |||
}, | |||
"run_exports": { | |||
"type": "array", | |||
"items": { | |||
"type": "string" | |||
} | |||
}, | |||
"ignore_run_exports": { | |||
"type": "array", | |||
"items": { | |||
"type": "string" | |||
} | |||
} | |||
} | |||
}, | |||
"requirements": { | |||
"$ref": "#/definitions/requirement" | |||
}, | |||
"test": { | |||
"anyOf": [ | |||
{ | |||
"type": "string" | |||
}, | |||
{ | |||
"$ref": "#/definitions/test" | |||
} | |||
] | |||
}, | |||
"outputs": { | |||
"type": "array", | |||
"items": { | |||
"type": "object", | |||
"properties": { | |||
"name": { | |||
"type": "string" | |||
}, | |||
"version": { | |||
"type": "string" | |||
}, | |||
"files": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/path" | |||
} | |||
}, | |||
"script": { | |||
"$ref": "#/definitions/path" | |||
}, | |||
"script_interpreter": { | |||
"type": "string" | |||
}, | |||
"requirements": { | |||
"oneOf": [ | |||
{ | |||
"$ref": "#/definitions/requirement" | |||
}, | |||
{ | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/package" | |||
} | |||
} | |||
] | |||
}, | |||
"run_exports": { | |||
"type": "array", | |||
"items": { | |||
"type": "string" | |||
} | |||
}, | |||
"test": { | |||
"$ref": "#/definitions/test" | |||
}, | |||
"type": { | |||
"type": "string" | |||
} | |||
} | |||
} | |||
}, | |||
"about": { | |||
"type": "object", | |||
"properties": { | |||
"home": { | |||
"$ref": "#/definitions/uri" | |||
}, | |||
"license": { | |||
"type": "string" | |||
}, | |||
"license_file": { | |||
"$ref": "#/definitions/path" | |||
}, | |||
"summary": { | |||
"type": "string" | |||
} | |||
} | |||
}, | |||
"app": { | |||
"type": "object", | |||
"properties": { | |||
"entry": { | |||
"type": "string" | |||
}, | |||
"icon": { | |||
"$ref": "#/definitions/path" | |||
}, | |||
"summary": { | |||
"type": "string" | |||
}, | |||
"own_environment": { | |||
"$ref": "#/definitions/boolean" | |||
} | |||
} | |||
}, | |||
"extra": {} | |||
} | |||
} |
@@ -1,143 +0,0 @@ | |||
{ | |||
"title": ".condarc", | |||
"description": "The conda configuration file; https://conda.io/docs/user-guide/configuration/use-condarc.html", | |||
"id": "https://raw.githubusercontent.com/Microsoft/vscode-python/master/schemas/condarc.json", | |||
"$schema": "http://json-schema.org/draft-04/schema#", | |||
"definitions": { | |||
"channel": { | |||
"type": "string" | |||
}, | |||
"feature": { | |||
"type": "string" | |||
}, | |||
"package": { | |||
"type": "string" | |||
}, | |||
"path": { | |||
"type": "string" | |||
} | |||
}, | |||
"properties": { | |||
"channels": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/channel" | |||
} | |||
}, | |||
"allow_other_channels": { | |||
"type": "boolean" | |||
}, | |||
"default_channels": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/channel" | |||
} | |||
}, | |||
"auto_update_conda": { | |||
"type": "boolean" | |||
}, | |||
"always_yes": { | |||
"type": "boolean" | |||
}, | |||
"show_channel_urls": { | |||
"type": "boolean" | |||
}, | |||
"changeps1": { | |||
"type": "boolean" | |||
}, | |||
"add_pip_as_python_dependency": { | |||
"type": "boolean" | |||
}, | |||
"use_pip": { | |||
"type": "boolean" | |||
}, | |||
"proxy_servers": { | |||
"type": "object", | |||
"additionalProperties": { | |||
"type": "string", | |||
"format": "hostname" | |||
} | |||
}, | |||
"ssl_verify": { | |||
"type": "boolean" | |||
}, | |||
"offline": { | |||
"type": "boolean" | |||
}, | |||
"allow_softlinks": { | |||
"type": "boolean" | |||
}, | |||
"channel_alias": { | |||
"type": "string", | |||
"format": "uri" | |||
}, | |||
"create_default_packages": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/package" | |||
} | |||
}, | |||
"track_features": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/feature" | |||
} | |||
}, | |||
"update_dependencies": { | |||
"type": "boolean" | |||
}, | |||
"disallow": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/package" | |||
} | |||
}, | |||
"add_anaconda_token": { | |||
"type": "boolean" | |||
}, | |||
"envs_dirs": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/path" | |||
} | |||
}, | |||
"anaconda_upload": { | |||
"type": "boolean" | |||
}, | |||
"conda-build": { | |||
"type": "object", | |||
"properties": { | |||
"root-dir": { | |||
"$ref": "#/definitions/path" | |||
}, | |||
"quiet": { | |||
"type": "boolean" | |||
}, | |||
"filename_hashing": { | |||
"type": "boolean" | |||
}, | |||
"no_verify": { | |||
"type": "boolean" | |||
}, | |||
"set_build_id": { | |||
"type": "boolean" | |||
}, | |||
"skip_existing": { | |||
"type": "boolean" | |||
}, | |||
"include_recipe": { | |||
"type": "boolean" | |||
}, | |||
"activate": { | |||
"type": "boolean" | |||
}, | |||
"pypirc": { | |||
"$ref": "#/definitions/path" | |||
}, | |||
"pypi_repository": { | |||
"type": "string" | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -1,243 +0,0 @@ | |||
{ | |||
"if": { | |||
"prefix": "if", | |||
"body": [ | |||
"if ${1:expression}:", | |||
"\t${2:pass}" | |||
], | |||
"description": "Code snippet for an if statement" | |||
}, | |||
"if/else": { | |||
"prefix": "if/else", | |||
"body": [ | |||
"if ${1:condition}:", | |||
"\t${2:pass}", | |||
"else:", | |||
"\t${3:pass}" | |||
], | |||
"description": "Code snippet for an if statement with else" | |||
}, | |||
"elif": { | |||
"prefix": "elif", | |||
"body": [ | |||
"elif ${1:expression}:", | |||
"\t${2:pass}" | |||
], | |||
"description": "Code snippet for an elif" | |||
}, | |||
"else": { | |||
"prefix": "else", | |||
"body": [ | |||
"else:", | |||
"\t${1:pass}" | |||
], | |||
"description": "Code snippet for an else" | |||
}, | |||
"while": { | |||
"prefix": "while", | |||
"body": [ | |||
"while ${1:expression}:", | |||
"\t${2:pass}" | |||
], | |||
"description": "Code snippet for a while loop" | |||
}, | |||
"while/else": { | |||
"prefix": "while/else", | |||
"body": [ | |||
"while ${1:expression}:", | |||
"\t${2:pass}", | |||
"else:", | |||
"\t${3:pass}" | |||
], | |||
"description": "Code snippet for a while loop with else" | |||
}, | |||
"for": { | |||
"prefix": "for", | |||
"body": [ | |||
"for ${1:target_list} in ${2:expression_list}:", | |||
"\t${3:pass}" | |||
], | |||
"description": "Code snippet for a for loop" | |||
}, | |||
"for/else": { | |||
"prefix": "for/else", | |||
"body": [ | |||
"for ${1:target_list} in ${2:expression_list}:", | |||
"\t${3:pass}", | |||
"else:", | |||
"\t${4:pass}" | |||
], | |||
"description": "Code snippet for a for loop with else" | |||
}, | |||
"try/except": { | |||
"prefix": "try/except", | |||
"body": [ | |||
"try:", | |||
"\t${1:pass}", | |||
"except ${2:expression} as ${3:identifier}:", | |||
"\t${4:pass}" | |||
], | |||
"description": "Code snippet for a try/except statement" | |||
}, | |||
"try/finally": { | |||
"prefix": "try/finally", | |||
"body": [ | |||
"try:", | |||
"\t${1:pass}", | |||
"finally:", | |||
"\t${2:pass}" | |||
], | |||
"description": "Code snippet for a try/finally statement" | |||
}, | |||
"try/except/else": { | |||
"prefix": "try/except/else", | |||
"body": [ | |||
"try:", | |||
"\t${1:pass}", | |||
"except ${2:expression} as ${3:identifier}:", | |||
"\t${4:pass}", | |||
"else:", | |||
"\t${5:pass}" | |||
], | |||
"description": "Code snippet for a try/except/else statement" | |||
}, | |||
"try/except/finally": { | |||
"prefix": "try/except/finally", | |||
"body": [ | |||
"try:", | |||
"\t${1:pass}", | |||
"except ${2:expression} as ${3:identifier}:", | |||
"\t${4:pass}", | |||
"finally:", | |||
"\t${5:pass}" | |||
], | |||
"description": "Code snippet for a try/except/finally statement" | |||
}, | |||
"try/except/else/finally": { | |||
"prefix": "try/except/else/finally", | |||
"body": [ | |||
"try:", | |||
"\t${1:pass}", | |||
"except ${2:expression} as ${3:identifier}:", | |||
"\t${4:pass}", | |||
"else:", | |||
"\t${5:pass}", | |||
"finally:", | |||
"\t${6:pass}" | |||
], | |||
"description": "Code snippet for a try/except/else/finally statement" | |||
}, | |||
"with": { | |||
"prefix": "with", | |||
"body": [ | |||
"with ${1:expression} as ${2:target}:", | |||
"\t${3:pass}" | |||
], | |||
"description": "Code snippet for a with statement" | |||
}, | |||
"def": { | |||
"prefix": "def", | |||
"body": [ | |||
"def ${1:funcname}(${2:parameter_list}):", | |||
"\t${3:pass}" | |||
], | |||
"description": "Code snippet for a function definition" | |||
}, | |||
"def(class method)": { | |||
"prefix": "def(class method)", | |||
"body": [ | |||
"def ${1:funcname}(self, ${2:parameter_list}):", | |||
"\t${3:pass}" | |||
], | |||
"description": "Code snippet for a class method" | |||
}, | |||
"def(static class method)": { | |||
"prefix": "def(static class method)", | |||
"body": [ | |||
"@staticmethod", | |||
"def ${1:funcname}(${2:parameter_list}):", | |||
"\t${3:pass}" | |||
], | |||
"description": "Code snippet for a static class method" | |||
}, | |||
"def(abstract class method)": { | |||
"prefix": "def(abstract class method)", | |||
"body": [ | |||
"def ${1:funcname}(self, ${2:parameter_list}):", | |||
"\traise NotImplementedError" | |||
], | |||
"description": "Code snippet for an abstract class method" | |||
}, | |||
"class": { | |||
"prefix": "class", | |||
"body": [ | |||
"class ${1:classname}(${2:object}):", | |||
"\t${3:pass}" | |||
], | |||
"description": "Code snippet for a class definition" | |||
}, | |||
"lambda": { | |||
"prefix": "lambda", | |||
"body": [ | |||
"lambda ${1:parameter_list}: ${2:expression}" | |||
], | |||
"description": "Code snippet for a lambda statement" | |||
}, | |||
"if(main)": { | |||
"prefix": "__main__", | |||
"body": [ | |||
"if __name__ == \"__main__\":", | |||
" ${1:pass}", | |||
], | |||
"description": "Code snippet for a `if __name__ == \"__main__\": ...` block" | |||
}, | |||
"async/def": { | |||
"prefix": "async/def", | |||
"body": [ | |||
"async def ${1:funcname}(${2:parameter_list}):", | |||
"\t${3:pass}" | |||
], | |||
"description": "Code snippet for an async statement" | |||
}, | |||
"async/for": { | |||
"prefix": "async/for", | |||
"body": [ | |||
"async for ${1:target} in ${2:iter}:", | |||
"\t${3:block}" | |||
], | |||
"description": "Code snippet for an async for statement" | |||
}, | |||
"async/for/else": { | |||
"prefix": "async/for/else", | |||
"body": [ | |||
"async for ${1:target} in ${2:iter}:", | |||
"\t${3:block}", | |||
"else:", | |||
"\t${4:block}" | |||
], | |||
"description": "Code snippet for an async for statement with else" | |||
}, | |||
"async/with": { | |||
"prefix": "async/with", | |||
"body": [ | |||
"async with ${1:expr} as ${2:var}:", | |||
"\t${3:block}" | |||
], | |||
"description": "Code snippet for an async with statement" | |||
}, | |||
"ipdb": { | |||
"prefix": "ipdb", | |||
"body": "import ipdb; ipdb.set_trace()", | |||
"description": "Code snippet for ipdb debug" | |||
}, | |||
"pdb": { | |||
"prefix": "pdb", | |||
"body": "import pdb; pdb.set_trace()", | |||
"description": "Code snippet for pdb debug" | |||
}, | |||
"pudb": { | |||
"prefix": "pudb", | |||
"body": "import pudb; pudb.set_trace()", | |||
"description": "Code snippet for pudb debug" | |||
}, | |||
} |
@@ -1,39 +0,0 @@ | |||
const path = require('path') | |||
module.exports = { | |||
entry: './src/index.ts', | |||
target: 'node', | |||
mode: 'none', | |||
resolve: { | |||
mainFields: ['module', 'main'], | |||
extensions: ['.js', '.ts'] | |||
}, | |||
externals: { | |||
'coc.nvim': 'commonjs coc.nvim' | |||
}, | |||
module: { | |||
rules: [{ | |||
test: /\.ts$/, | |||
exclude: /node_modules/, | |||
use: [{ | |||
loader: 'ts-loader', | |||
options: { | |||
compilerOptions: { | |||
"sourceMap": true, | |||
} | |||
} | |||
}] | |||
}] | |||
}, | |||
output: { | |||
path: path.join(__dirname, 'lib'), | |||
filename: 'index.js', | |||
libraryTarget: "commonjs", | |||
}, | |||
plugins: [ | |||
], | |||
node: { | |||
__dirname: false, | |||
__filename: false | |||
} | |||
} |
@@ -1,187 +0,0 @@ | |||
# coc-snippets | |||
Snippets solution for [coc.nvim](https://github.com/neoclide/coc.nvim) | |||
![2019-03-23 00_09_39](https://user-images.githubusercontent.com/251450/54837017-62891300-4d00-11e9-9e53-49742a1a33f2.gif) | |||
_Snippet preview requires [neovim 0.4 or latest vim8](https://github.com/neoclide/coc.nvim/wiki/F.A.Q#how-to-make-preview-window-shown-aside-with-pum)_ | |||
It's capable of: | |||
- Load UltiSnips snippets. | |||
- Load snipmate snippets. | |||
- Load VSCode snippets from coc extensions. | |||
- Load VSCode snippets from custom directories. | |||
- Load UltiSnips snippets from configured folder. | |||
- Provide snippets as completion items. | |||
- Provide expand and expandOrJump keymaps for snippet. | |||
- Provide snippets list for edit snippet. | |||
- Provide `snippets.editSnippets` command for edit user snippets of current filetype. | |||
**Note:** some features of ultisnips and snipmate format snippets not supported, checkout [faq](#faq). | |||
## Why? | |||
- Use same keys for jump placeholder. | |||
- Nested snippet support. | |||
- Always async, never slows you down. | |||
- Improved match for complete items with TextEdit support. | |||
- Edit snippets of current buffer by `:CocList snippets`, sorted by mru. | |||
## Install | |||
In your vim/neovim, run command: | |||
``` | |||
:CocInstall coc-snippets | |||
``` | |||
## Examples | |||
```vim | |||
" Use <C-l> for trigger snippet expand. | |||
imap <C-l> <Plug>(coc-snippets-expand) | |||
" Use <C-j> for select text for visual placeholder of snippet. | |||
vmap <C-j> <Plug>(coc-snippets-select) | |||
" Use <C-j> for jump to next placeholder, it's default of coc.nvim | |||
let g:coc_snippet_next = '<c-j>' | |||
" Use <C-k> for jump to previous placeholder, it's default of coc.nvim | |||
let g:coc_snippet_prev = '<c-k>' | |||
" Use <C-j> for both expand and jump (make expand higher priority.) | |||
imap <C-j> <Plug>(coc-snippets-expand-jump) | |||
``` | |||
Make `<tab>` used for trigger completion, completion confirm, snippet expand and jump like VSCode. | |||
```vim | |||
inoremap <silent><expr> <TAB> | |||
\ pumvisible() ? coc#_select_confirm() : | |||
\ coc#expandableOrJumpable() ? "\<C-r>=coc#rpc#request('doKeymap', ['snippets-expand-jump',''])\<CR>" : | |||
\ <SID>check_back_space() ? "\<TAB>" : | |||
\ coc#refresh() | |||
function! s:check_back_space() abort | |||
let col = col('.') - 1 | |||
return !col || getline('.')[col - 1] =~# '\s' | |||
endfunction | |||
let g:coc_snippet_next = '<tab>' | |||
``` | |||
**Note:** `coc#_select_confirm()` helps select first complete item when there's | |||
no complete item selected, neovim 0.4 or latest vim8 required for this function | |||
work as expected. | |||
## Ultisnips features | |||
Some ultisnips features are **not** supported: | |||
- [x] Position check of trigger option, including `b`, `w` and `i`. | |||
- [x] Execute vim, python and shell code in snippet. | |||
- [x] `extends`, `priority` and `clearsnippets` command in snippet file. | |||
- [x] Visual placeholder. | |||
- [x] Placeholder and variable transform. | |||
- [x] Expression snippet. | |||
- [x] Automatic trigger snippet. | |||
- [x] Context snippets. | |||
- [x] Support loading snipmate snippets. | |||
- [ ] Execute shell code with custom shabang (will not support). | |||
- [ ] Automatic reformat snippet after change of placeholder (can't support). | |||
- [ ] Format related snippet options, including `t`, `s` and `m` (can't support). | |||
- [ ] Snippet actions (can't support). | |||
**Note**: python regex in snippet are converted to javascript regex, however, | |||
some regex patterns can't be supported by javascript, including | |||
`\u` `(?s)` `\Z` `(?(id/name)yes-pattern|no-pattern)`. | |||
## Options | |||
- `snippets.priority`: priority of snippets source, default `90`. | |||
- `snippets.editSnippetsCommand`: Open command used for snippets.editSnippets command, use coc.preferences.jumpCommand by default. | |||
- `snippets.trace`: Trace level of snippets channel. | |||
- `snippets.enableStatusItem`: Enable status item in `g:coc_status` used for statusline. | |||
- `snippets.extends`: extends filetype's snippets with other filetypes, example: | |||
```json | |||
{ | |||
"cpp": ["c"], | |||
"javascriptreact": ["javascript"], | |||
"typescript": ["javascript"] | |||
} | |||
``` | |||
- `snippets.userSnippetsDirectory`, Directory that contains custom user ultisnips snippets, use ultisnips in extension root by default. | |||
- `snippets.shortcut`, shortcut in completion menu, default `S`. | |||
- `snippets.autoTrigger`: enable auto trigger for auto trigger ultisnips snippets, default `true`. | |||
- `snippets.triggerCharacters`: trigger characters for completion, default `[]`. | |||
- `snippets.loadFromExtensions`: load snippets from coc.nvim extensions, default: `true`. | |||
- `snippets.convertToSnippetsAction`: Add convert to snippet to code action list. | |||
- `snippets.textmateSnippetsRoots`: absolute directories that contains textmate/VSCode snippets to load. | |||
- `snippets.ultisnips.enable`: enable load UltiSnips snippets, default `true`. | |||
- `snippets.ultisnips.usePythonx`: use `pythonx` for eval python code when possible, default `true`. | |||
- `snippets.ultisnips.pythonVersion`: when `usePythonx` is false, python version to use for | |||
python code, default to `3`. | |||
- `snippets.ultisnips.directories`: directories that searched for snippet files, | |||
could be subfolder in every \$runtimepath or absolute paths, default: `["UltiSnips"]` | |||
- `snippets.snipmate.enable`: enable load snipmate snippets, default `true`. | |||
- `snippets.snippets.author`: author name used for `g:snips_author` | |||
## Commands | |||
- Use `:CocList snippets` to open snippets list. | |||
- Use `:CocCommand snippets.editSnippets` to edit user snippet of current filetype. | |||
- Use `:CocCommand snippets.openSnippetFiles` to open snippet files of current filetype. | |||
## F.A.Q | |||
**Q:** How to check if a snippet successfully loaded? | |||
**A:** Use command `:CocCommand workspace.showOutput snippets` | |||
**Q:** Some ultisnips snippet not works as expected. | |||
**A:** Reformat after change of placeholder feature can't be supported for now, | |||
and some regex pattern can't be converted to javascript regex pattern, so the | |||
snippet can be failed to load. | |||
**Q:** Where to get snippets? | |||
**A:** One solution is install [honza/vim-snippets](https://github.com/honza/vim-snippets) which is widely used. | |||
**Q:** Do I need to install [Ultisnips](https://github.com/SirVer/ultisnips). | |||
**A:** No! This extension is designed to work with or without Ultisnips, you can | |||
still install Ultisnips, but this extension would not run any code or read | |||
configuration from it. | |||
**Q:** How to check jumpable or expandable at current position. | |||
**A:** Use functions provided by coc.nvim: `coc#expandable()` `coc#jumpable()` and `coc#expandableOrJumpable()`. | |||
**Q:** It doesn't load snippets from [vim-go](https://github.com/fatih/vim-go). | |||
**A:** It uses `g:UltiSnipsSnippetDirectories` which is not supported, you can | |||
add settings: | |||
``` | |||
snippets.ultisnips.directories: [ | |||
"UltiSnips", | |||
"gosnippets/UltiSnips" | |||
], | |||
``` | |||
to load it. | |||
**Q:** How could I add custom UltiSnips snippets. | |||
**A:** You can create snippet files in folder: `$VIMCONFIG/coc/ultisnips`, use | |||
command `:CocCommand snippets.editSnippets` to open user snippet of current | |||
filetype. | |||
## License | |||
MIT |
@@ -1,23 +0,0 @@ | |||
" Set some sane defaults for snippet files | |||
if exists('b:did_ftplugin') | |||
finish | |||
endif | |||
let b:did_ftplugin = 1 | |||
" Fold by syntax, but open all folds by default | |||
setlocal foldmethod=syntax | |||
setlocal foldlevel=99 | |||
setlocal commentstring=#%s | |||
setlocal noexpandtab | |||
setlocal autoindent nosmartindent nocindent | |||
" Define match words for use with matchit plugin | |||
" http://www.vim.org/scripts/script.php?script_id=39 | |||
if exists("loaded_matchit") && !exists("b:match_words") | |||
let b:match_ignorecase = 0 | |||
let b:match_words = '^snippet\>:^endsnippet\>,^global\>:^endglobal\>,\${:}' | |||
let s:set_match_words = 1 | |||
endif |
@@ -1,185 +0,0 @@ | |||
{ | |||
"name": "coc-snippets", | |||
"version": "2.1.28", | |||
"description": "Snippets extension for coc.nvim", | |||
"main": "lib/index.js", | |||
"publisher": "chemzqm", | |||
"keywords": [ | |||
"coc.nvim", | |||
"snippets", | |||
"colors" | |||
], | |||
"engines": { | |||
"coc": "^0.0.61" | |||
}, | |||
"scripts": { | |||
"clean": "rimraf lib", | |||
"build": "webpack", | |||
"prepare": "npx npm-run-all clean build" | |||
}, | |||
"activationEvents": [ | |||
"*" | |||
], | |||
"contributes": { | |||
"commands": [ | |||
{ | |||
"title": "Edit user snippets file of current document filetype.", | |||
"command": "snippets.editSnippets" | |||
}, | |||
{ | |||
"title": "Edit snippets files of current document filetype.", | |||
"command": "snippets.openSnippetFiles" | |||
} | |||
], | |||
"configuration": { | |||
"type": "object", | |||
"properties": { | |||
"snippets.priority": { | |||
"type": "number", | |||
"default": 90, | |||
"description": "Completion source priority of snippets." | |||
}, | |||
"snippets.editSnippetsCommand": { | |||
"type": "string", | |||
"default": "", | |||
"description": "Open command used for snippets.editSnippets command, use coc.preferences.jumpCommand by default." | |||
}, | |||
"snippets.trace": { | |||
"type": "string", | |||
"default": "error", | |||
"enum": [ | |||
"error", | |||
"verbose" | |||
], | |||
"description": "Trace level of snippets channel." | |||
}, | |||
"snippets.enableStatusItem": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Enable status item in g:coc_status used for statusline." | |||
}, | |||
"snippets.loadFromExtensions": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Enable load snippets from extensions." | |||
}, | |||
"snippets.textmateSnippetsRoots": { | |||
"type": "array", | |||
"default": [], | |||
"description": "List of directories that contains textmate/VSCode snippets to load.", | |||
"items": { | |||
"type": "string" | |||
} | |||
}, | |||
"snippets.extends": { | |||
"type": "object", | |||
"default": {}, | |||
"description": "Configure filetypes to inherit with, ex: {\"cpp\": [\"c\"], \"javascriptreact\": [\"javascript\"]}" | |||
}, | |||
"snippets.userSnippetsDirectory": { | |||
"type": "string", | |||
"default": "", | |||
"description": "Directory that contains custom user ultisnips snippets, use ultisnips in extension root by default." | |||
}, | |||
"snippets.shortcut": { | |||
"type": "string", | |||
"default": "S", | |||
"description": "Shortcut in completion menu." | |||
}, | |||
"snippets.triggerCharacters": { | |||
"type": "array", | |||
"default": [], | |||
"description": "Trigger characters for trigger snippets completion.", | |||
"items": { | |||
"type": "string" | |||
} | |||
}, | |||
"snippets.autoTrigger": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Enable trigger auto trigger snippet after type character." | |||
}, | |||
"snippets.convertToSnippetsAction": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Add convert to snippet to code action list." | |||
}, | |||
"snippets.ultisnips.enable": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Enable load snippets from ultisnips folders." | |||
}, | |||
"snippets.ultisnips.usePythonx": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Use :pyx command for python code when possible." | |||
}, | |||
"snippets.ultisnips.pythonVersion": { | |||
"type": "number", | |||
"default": 3, | |||
"description": "Python version used for python code when not using pyx." | |||
}, | |||
"snippets.ultisnips.directories": { | |||
"type": "array", | |||
"default": [ | |||
"UltiSnips" | |||
], | |||
"description": "Directories that searched for snippet files, could be directory as subfolder in $runtimepath or absolute paths.", | |||
"items": { | |||
"type": "string" | |||
} | |||
}, | |||
"snippets.snipmate.enable": { | |||
"type": "boolean", | |||
"default": true, | |||
"description": "Load snipmate snippets from snippets directory in runtimepath." | |||
}, | |||
"snippets.snipmate.author": { | |||
"type": "string", | |||
"default": "", | |||
"description": "Author name used for g:snips_author" | |||
} | |||
} | |||
} | |||
}, | |||
"jest": { | |||
"testEnvironment": "node", | |||
"moduleFileExtensions": [ | |||
"ts", | |||
"tsx", | |||
"json", | |||
"js" | |||
], | |||
"transform": { | |||
"^.+\\.tsx?$": "ts-jest" | |||
}, | |||
"testRegex": "tests/.*\\.ts$" | |||
}, | |||
"author": "chemzqm@gmail.com", | |||
"license": "MIT", | |||
"devDependencies": { | |||
"@chemzqm/tsconfig": "^0.0.3", | |||
"@chemzqm/tslint-config": "^1.0.18", | |||
"@types/jest": "^25.1.4", | |||
"@types/node": "^13.9.1", | |||
"@types/pify": "^3.0.2", | |||
"@types/uuid": "^7.0.0", | |||
"@types/which": "^1.3.2", | |||
"coc.nvim": "^0.0.77", | |||
"debounce": "^1.2.0", | |||
"jest": "^25.1.0", | |||
"jsonc-parser": "^2.2.1", | |||
"pify": "^5.0.0", | |||
"rimraf": "^3.0.2", | |||
"ts-jest": "^25.2.1", | |||
"ts-loader": "^6.2.1", | |||
"tslint": "^6.1.0", | |||
"typescript": "^3.8.3", | |||
"uuid": "^7.0.2", | |||
"vscode-languageserver-protocol": "^3.15.3", | |||
"webpack": "^4.42.0", | |||
"webpack-cli": "^3.3.11", | |||
"which": "^2.0.2" | |||
}, | |||
"dependencies": {} | |||
} |
@@ -1,268 +0,0 @@ | |||
import re, os, vim, string, random | |||
from collections import deque, namedtuple | |||
_Placeholder = namedtuple("_FrozenPlaceholder", ["current_text", "start", "end"]) | |||
_VisualContent = namedtuple("_VisualContent", ["mode", "text"]) | |||
_Position = namedtuple("_Position", ["line", "col"]) | |||
class _SnippetUtilCursor(object): | |||
def __init__(self, cursor): | |||
self._cursor = [cursor[0] - 1, cursor[1]] | |||
self._set = False | |||
def preserve(self): | |||
self._set = True | |||
self._cursor = [vim.buf.cursor[0], vim.buf.cursor[1]] | |||
def is_set(self): | |||
return self._set | |||
def set(self, line, column): | |||
self.__setitem__(0, line) | |||
self.__setitem__(1, column) | |||
def to_vim_cursor(self): | |||
return (self._cursor[0] + 1, self._cursor[1]) | |||
def __getitem__(self, index): | |||
return self._cursor[index] | |||
def __setitem__(self, index, value): | |||
self._set = True | |||
self._cursor[index] = value | |||
def __len__(self): | |||
return 2 | |||
def __str__(self): | |||
return str((self._cursor[0], self._cursor[1])) | |||
class IndentUtil(object): | |||
"""Utility class for dealing properly with indentation.""" | |||
def __init__(self): | |||
self.reset() | |||
def reset(self): | |||
"""Gets the spacing properties from Vim.""" | |||
self.shiftwidth = int( | |||
vim.eval("exists('*shiftwidth') ? shiftwidth() : &shiftwidth") | |||
) | |||
self._expandtab = vim.eval("&expandtab") == "1" | |||
self._tabstop = int(vim.eval("&tabstop")) | |||
def ntabs_to_proper_indent(self, ntabs): | |||
"""Convert 'ntabs' number of tabs to the proper indent prefix.""" | |||
line_ind = ntabs * self.shiftwidth * " " | |||
line_ind = self.indent_to_spaces(line_ind) | |||
line_ind = self.spaces_to_indent(line_ind) | |||
return line_ind | |||
def indent_to_spaces(self, indent): | |||
"""Converts indentation to spaces respecting Vim settings.""" | |||
indent = indent.expandtabs(self._tabstop) | |||
right = (len(indent) - len(indent.rstrip(" "))) * " " | |||
indent = indent.replace(" ", "") | |||
indent = indent.replace("\t", " " * self._tabstop) | |||
return indent + right | |||
def spaces_to_indent(self, indent): | |||
"""Converts spaces to proper indentation respecting Vim settings.""" | |||
if not self._expandtab: | |||
indent = indent.replace(" " * self._tabstop, "\t") | |||
return indent | |||
class SnippetUtil(object): | |||
"""Provides easy access to indentation, etc. | |||
This is the 'snip' object in python code. | |||
""" | |||
def __init__(self, _initial_indent, start, end, context): | |||
self._ind = IndentUtil() | |||
self._visual = _VisualContent( | |||
vim.eval("visualmode()"), vim.eval('get(g:,"coc_selected_text","")') | |||
) | |||
self._initial_indent = _initial_indent | |||
self._reset("") | |||
self._start = start | |||
self._end = end | |||
self._context = context | |||
def _reset(self, cur): | |||
"""Gets the snippet ready for another update. | |||
:cur: the new value for c. | |||
""" | |||
self._ind.reset() | |||
self._cur = cur | |||
self._rv = "" | |||
self._changed = False | |||
self.reset_indent() | |||
def shift(self, amount=1): | |||
"""Shifts the indentation level. Note that this uses the shiftwidth | |||
because thats what code formatters use. | |||
:amount: the amount by which to shift. | |||
""" | |||
self.indent += " " * self._ind.shiftwidth * amount | |||
def unshift(self, amount=1): | |||
"""Unshift the indentation level. Note that this uses the shiftwidth | |||
because thats what code formatters use. | |||
:amount: the amount by which to unshift. | |||
""" | |||
by = -self._ind.shiftwidth * amount | |||
try: | |||
self.indent = self.indent[:by] | |||
except IndexError: | |||
self.indent = "" | |||
def mkline(self, line="", indent=""): | |||
"""Creates a properly set up line. | |||
:line: the text to add | |||
:indent: the indentation to have at the beginning | |||
if None, it uses the default amount | |||
""" | |||
return indent + line | |||
def reset_indent(self): | |||
"""Clears the indentation.""" | |||
self.indent = self._initial_indent | |||
# Utility methods | |||
@property | |||
def fn(self): # pylint:disable=no-self-use,invalid-name | |||
"""The filename.""" | |||
return vim.eval('expand("%:t")') or "" | |||
@property | |||
def basename(self): # pylint:disable=no-self-use | |||
"""The filename without extension.""" | |||
return vim.eval('expand("%:t:r")') or "" | |||
@property | |||
def ft(self): # pylint:disable=invalid-name | |||
"""The filetype.""" | |||
return self.opt("&filetype", "") | |||
@property | |||
def rv(self): # pylint:disable=invalid-name | |||
"""The return value. | |||
The text to insert at the location of the placeholder. | |||
""" | |||
return self._rv | |||
@rv.setter | |||
def rv(self, value): # pylint:disable=invalid-name | |||
"""See getter.""" | |||
self._changed = True | |||
self._rv = value | |||
@property | |||
def _rv_changed(self): | |||
"""True if rv has changed.""" | |||
return self._changed | |||
@property | |||
def c(self): # pylint:disable=invalid-name | |||
"""The current text of the placeholder.""" | |||
return "" | |||
@property | |||
def v(self): # pylint:disable=invalid-name | |||
"""Content of visual expansions.""" | |||
return self._visual | |||
@property | |||
def p(self): | |||
if "coc_last_placeholder" in vim.vars: | |||
p = vim.vars["coc_last_placeholder"] | |||
start = _Position(p["start"]["line"], p["start"]["col"]) | |||
end = _Position(p["end"]["line"], p["end"]["col"]) | |||
return _Placeholder(p["current_text"], start, end) | |||
return None | |||
@property | |||
def context(self): | |||
return self._context | |||
def opt(self, option, default=None): # pylint:disable=no-self-use | |||
"""Gets a Vim variable.""" | |||
if vim.eval("exists('%s')" % option) == "1": | |||
try: | |||
return vim.eval(option) | |||
except vim.error: | |||
pass | |||
return default | |||
def __add__(self, value): | |||
"""Appends the given line to rv using mkline.""" | |||
self.rv += "\n" # pylint:disable=invalid-name | |||
self.rv += self.mkline(value) | |||
return self | |||
def __lshift__(self, other): | |||
"""Same as unshift.""" | |||
self.unshift(other) | |||
def __rshift__(self, other): | |||
"""Same as shift.""" | |||
self.shift(other) | |||
@property | |||
def snippet_start(self): | |||
""" | |||
Returns start of the snippet in format (line, column). | |||
""" | |||
return self._start | |||
@property | |||
def snippet_end(self): | |||
""" | |||
Returns end of the snippet in format (line, column). | |||
""" | |||
return self._end | |||
@property | |||
def buffer(self): | |||
return vim.buf | |||
class ContextSnippet(object): | |||
def __init__(self): | |||
self.buffer = vim.current.buffer | |||
self.window = vim.current.window | |||
self.cursor = _SnippetUtilCursor(vim.current.window.cursor) | |||
self.line = vim.call("line", ".") - 1 | |||
self.column = vim.call("col", ".") - 1 | |||
line = vim.call("getline", ".") | |||
self.after = line[self.column :] | |||
if "coc_selected_text" in vim.vars: | |||
self.visual_mode = vim.eval("visualmode()") | |||
self.visual_text = vim.vars["coc_selected_text"] | |||
else: | |||
self.visual_mode = None | |||
self.visual_text = "" | |||
if "coc_last_placeholder" in vim.vars: | |||
p = vim.vars["coc_last_placeholder"] | |||
start = _Position(p["start"]["line"], p["start"]["col"]) | |||
end = _Position(p["end"]["line"], p["end"]["col"]) | |||
self.last_placeholder = _Placeholder(p["current_text"], start, end) | |||
else: | |||
self.last_placeholder = None |