mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge VS Code 1.21 source code (#1067)
* Initial VS Code 1.21 file copy with patches * A few more merges * Post npm install * Fix batch of build breaks * Fix more build breaks * Fix more build errors * Fix more build breaks * Runtime fixes 1 * Get connection dialog working with some todos * Fix a few packaging issues * Copy several node_modules to package build to fix loader issues * Fix breaks from master * A few more fixes * Make tests pass * First pass of license header updates * Second pass of license header updates * Fix restore dialog issues * Remove add additional themes menu items * fix select box issues where the list doesn't show up * formatting * Fix editor dispose issue * Copy over node modules to correct location on all platforms
This commit is contained in:
@@ -15,38 +15,6 @@
|
||||
"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."
|
||||
]
|
||||
},
|
||||
{
|
||||
"isLicense": true,
|
||||
"name": "markdown-it-named-headers",
|
||||
"licenseDetail": [
|
||||
"Copyright (c) markdown-it-named-headers authors",
|
||||
"",
|
||||
"This is free and unencumbered software released into the public domain.",
|
||||
"",
|
||||
"Anyone is free to copy, modify, publish, use, compile, sell, or",
|
||||
"distribute this software, either in source code form or as a compiled",
|
||||
"binary, for any purpose, commercial or non-commercial, and by any",
|
||||
"means.",
|
||||
"",
|
||||
"In jurisdictions that recognize copyright laws, the author or authors",
|
||||
"of this software dedicate any and all copyright interest in the",
|
||||
"software to the public domain. We make this dedication for the benefit",
|
||||
"of the public at large and to the detriment of our heirs and",
|
||||
"successors. We intend this dedication to be an overt act of",
|
||||
"relinquishment in perpetuity of all present and future rights to this",
|
||||
"software under copyright law.",
|
||||
"",
|
||||
"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 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.",
|
||||
"",
|
||||
"For more information, please refer to <http://unlicense.org/>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "textmate/markdown.tmbundle",
|
||||
"version": "0.0.0",
|
||||
@@ -67,23 +35,4 @@
|
||||
"to the base-name name of the original file, and an extension of txt, html, or similar. For example",
|
||||
"\"tidy\" is accompanied by \"tidy-license.txt\"."
|
||||
]
|
||||
},
|
||||
{
|
||||
"isLicense": true,
|
||||
"name": "uc.micro",
|
||||
"licenseDetail": [
|
||||
" DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE",
|
||||
" Version 2, December 2004",
|
||||
"",
|
||||
" Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>",
|
||||
"",
|
||||
" Everyone is permitted to copy and distribute verbatim or modified",
|
||||
" copies of this license document, and changing it is allowed as long",
|
||||
" as the name is changed.",
|
||||
"",
|
||||
" DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE",
|
||||
" TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION",
|
||||
"",
|
||||
" 0. You just DO WHAT THE FUCK YOU WANT TO."
|
||||
]
|
||||
}]
|
||||
|
||||
BIN
extensions/markdown/icon.png
Normal file
BIN
extensions/markdown/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"comments": {
|
||||
// symbols used for start and end a block comment. Remove this entry if your language does not support block comments
|
||||
"blockComment": [
|
||||
"<!--",
|
||||
"-->"
|
||||
]
|
||||
},
|
||||
// symbols used as brackets
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"]
|
||||
],
|
||||
"autoClosingPairs": [
|
||||
{
|
||||
"open": "{",
|
||||
"close": "}"
|
||||
},
|
||||
{
|
||||
"open": "[",
|
||||
"close": "]"
|
||||
},
|
||||
{
|
||||
"open": "(",
|
||||
"close": ")"
|
||||
},
|
||||
{
|
||||
"open": "<",
|
||||
"close": ">",
|
||||
"notIn": [
|
||||
"string"
|
||||
]
|
||||
}
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["(", ")"],
|
||||
["[", "]"],
|
||||
["`", "`"]
|
||||
],
|
||||
"folding": {
|
||||
"offSide": true
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
return;
|
||||
}
|
||||
didShow = true;
|
||||
const args = [settings.previewUri];
|
||||
|
||||
const notification = document.createElement('a');
|
||||
notification.innerText = strings.cspAlertMessageText;
|
||||
@@ -24,9 +23,17 @@
|
||||
notification.setAttribute('title', strings.cspAlertMessageTitle);
|
||||
|
||||
notification.setAttribute('role', 'button');
|
||||
notification.setAttribute('aria-label', strings.cspAlertMessageLabel);
|
||||
notification.setAttribute('href', `command:markdown.showPreviewSecuritySelector?${encodeURIComponent(JSON.stringify(args))}`);
|
||||
|
||||
notification.setAttribute('aria-label', strings.cspAlertMessageLabel);
|
||||
notification.onclick = () => {
|
||||
window.parent.postMessage({
|
||||
type: 'command',
|
||||
source: settings.source,
|
||||
body: {
|
||||
command: 'markdown.showPreviewSecuritySelector',
|
||||
args: [settings.source]
|
||||
}
|
||||
}, '*');
|
||||
};
|
||||
document.body.appendChild(notification);
|
||||
};
|
||||
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
|
||||
(function () {
|
||||
const unloadedStyles = [];
|
||||
|
||||
const settings = JSON.parse(document.getElementById('vscode-markdown-preview-data').getAttribute('data-settings'));
|
||||
|
||||
const onStyleLoadError = (event) => {
|
||||
const source = event.target.dataset.source;
|
||||
unloadedStyles.push(source);
|
||||
@@ -27,10 +28,13 @@
|
||||
if (!unloadedStyles.length) {
|
||||
return;
|
||||
}
|
||||
const args = [unloadedStyles];
|
||||
window.parent.postMessage({
|
||||
command: 'did-click-link',
|
||||
data: `command:_markdown.onPreviewStyleLoadError?${encodeURIComponent(JSON.stringify(args))}`
|
||||
}, 'file://');
|
||||
type: 'command',
|
||||
source: settings.source,
|
||||
body: {
|
||||
command: '_markdown.onPreviewStyleLoadError',
|
||||
args: [unloadedStyles]
|
||||
}
|
||||
}, '*');
|
||||
});
|
||||
}());
|
||||
@@ -2,7 +2,7 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// @ts-check
|
||||
'use strict';
|
||||
|
||||
(function () {
|
||||
@@ -29,24 +29,88 @@
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} min
|
||||
* @param {number} max
|
||||
* @param {number} value
|
||||
*/
|
||||
function clamp(min, max, value) {
|
||||
return Math.min(max, Math.max(min, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} line
|
||||
*/
|
||||
function clampLine(line) {
|
||||
return clamp(0, settings.lineCount - 1, line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post a message to the markdown extension
|
||||
*
|
||||
* @param {string} type
|
||||
* @param {object} body
|
||||
*/
|
||||
function postMessage(type, body) {
|
||||
window.parent.postMessage({
|
||||
type,
|
||||
source: settings.source,
|
||||
body
|
||||
}, '*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Post a command to be executed to the markdown extension
|
||||
*
|
||||
* @param {string} command
|
||||
* @param {any[]} args
|
||||
*/
|
||||
function postCommand(command, args) {
|
||||
postMessage('command', { command, args });
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {{ element: Element, line: number }} CodeLineElement
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return {CodeLineElement[]}
|
||||
*/
|
||||
const getCodeLineElements = (() => {
|
||||
/** @type {CodeLineElement[]} */
|
||||
let elements;
|
||||
return () => {
|
||||
if (!elements) {
|
||||
elements = Array.prototype.map.call(
|
||||
document.getElementsByClassName('code-line'),
|
||||
element => {
|
||||
const line = +element.getAttribute('data-line');
|
||||
return { element, line }
|
||||
})
|
||||
.filter(x => !isNaN(x.line));
|
||||
}
|
||||
return elements;
|
||||
};
|
||||
})()
|
||||
|
||||
/**
|
||||
* Find the html elements that map to a specific target line in the editor.
|
||||
*
|
||||
* If an exact match, returns a single element. If the line is between elements,
|
||||
* returns the element prior to and the element after the given line.
|
||||
*
|
||||
* @param {number} targetLine
|
||||
*
|
||||
* @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
|
||||
*/
|
||||
function getElementsForSourceLine(targetLine) {
|
||||
const lines = document.getElementsByClassName('code-line');
|
||||
let previous = lines[0] && +lines[0].getAttribute('data-line') ? { line: +lines[0].getAttribute('data-line'), element: lines[0] } : null;
|
||||
for (const element of lines) {
|
||||
const lineNumber = +element.getAttribute('data-line');
|
||||
if (isNaN(lineNumber)) {
|
||||
continue;
|
||||
}
|
||||
const entry = { line: lineNumber, element: element };
|
||||
if (lineNumber === targetLine) {
|
||||
const lineNumber = Math.floor(targetLine)
|
||||
const lines = getCodeLineElements();
|
||||
let previous = lines[0] || null;
|
||||
for (const entry of lines) {
|
||||
if (entry.line === lineNumber) {
|
||||
return { previous: entry, next: null };
|
||||
} else if (lineNumber > targetLine) {
|
||||
} else if (entry.line > lineNumber) {
|
||||
return { previous, next: entry };
|
||||
}
|
||||
previous = entry;
|
||||
@@ -56,71 +120,91 @@
|
||||
|
||||
/**
|
||||
* Find the html elements that are at a specific pixel offset on the page.
|
||||
*
|
||||
* @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
|
||||
*/
|
||||
function getLineElementsAtPageOffset(offset) {
|
||||
const lines = document.getElementsByClassName('code-line');
|
||||
const position = offset - window.scrollY;
|
||||
let previous = null;
|
||||
for (const element of lines) {
|
||||
const line = +element.getAttribute('data-line');
|
||||
if (isNaN(line)) {
|
||||
continue;
|
||||
}
|
||||
const bounds = element.getBoundingClientRect();
|
||||
const entry = { element, line };
|
||||
if (position < bounds.top) {
|
||||
if (previous && previous.fractional < 1) {
|
||||
previous.line += previous.fractional;
|
||||
return { previous };
|
||||
}
|
||||
return { previous, next: entry };
|
||||
}
|
||||
entry.fractional = (position - bounds.top) / (bounds.height);
|
||||
previous = entry;
|
||||
}
|
||||
return { previous };
|
||||
}
|
||||
const lines = getCodeLineElements()
|
||||
|
||||
function getSourceRevealAddedOffset() {
|
||||
return -(window.innerHeight * 1 / 5);
|
||||
const position = offset - window.scrollY;
|
||||
|
||||
let lo = -1;
|
||||
let hi = lines.length - 1;
|
||||
while (lo + 1 < hi) {
|
||||
const mid = Math.floor((lo + hi) / 2);
|
||||
const bounds = lines[mid].element.getBoundingClientRect();
|
||||
if (bounds.top + bounds.height >= position) {
|
||||
hi = mid;
|
||||
} else {
|
||||
lo = mid;
|
||||
}
|
||||
}
|
||||
|
||||
const hiElement = lines[hi];
|
||||
const hiBounds = hiElement.element.getBoundingClientRect();
|
||||
|
||||
if (hi >= 1 && hiBounds.top > position) {
|
||||
const loElement = lines[lo];
|
||||
return { previous: loElement, next: hiElement };
|
||||
}
|
||||
|
||||
return { previous: hiElement };
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to reveal the element for a source line in the editor.
|
||||
*
|
||||
* @param {number} line
|
||||
*/
|
||||
function scrollToRevealSourceLine(line) {
|
||||
const { previous, next } = getElementsForSourceLine(line);
|
||||
marker.update(previous && previous.element);
|
||||
if (previous && settings.scrollPreviewWithEditorSelection) {
|
||||
if (previous && settings.scrollPreviewWithEditor) {
|
||||
let scrollTo = 0;
|
||||
if (next) {
|
||||
const rect = previous.element.getBoundingClientRect();
|
||||
const previousTop = rect.top;
|
||||
|
||||
if (next && next.line !== previous.line) {
|
||||
// Between two elements. Go to percentage offset between them.
|
||||
const betweenProgress = (line - previous.line) / (next.line - previous.line);
|
||||
const elementOffset = next.element.getBoundingClientRect().top - previous.element.getBoundingClientRect().top;
|
||||
scrollTo = previous.element.getBoundingClientRect().top + betweenProgress * elementOffset;
|
||||
const elementOffset = next.element.getBoundingClientRect().top - previousTop;
|
||||
scrollTo = previousTop + betweenProgress * elementOffset;
|
||||
} else {
|
||||
scrollTo = previous.element.getBoundingClientRect().top;
|
||||
scrollTo = previousTop;
|
||||
}
|
||||
window.scroll(0, window.scrollY + scrollTo + getSourceRevealAddedOffset());
|
||||
|
||||
window.scroll(0, Math.max(1, window.scrollY + scrollTo));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} offset
|
||||
*/
|
||||
function getEditorLineNumberForPageOffset(offset) {
|
||||
const { previous, next } = getLineElementsAtPageOffset(offset);
|
||||
if (previous) {
|
||||
const previousBounds = previous.element.getBoundingClientRect();
|
||||
const offsetFromPrevious = (offset - window.scrollY - previousBounds.top);
|
||||
|
||||
if (next) {
|
||||
const betweenProgress = (offset - window.scrollY - previous.element.getBoundingClientRect().top) / (next.element.getBoundingClientRect().top - previous.element.getBoundingClientRect().top);
|
||||
return previous.line + betweenProgress * (next.line - previous.line);
|
||||
const progressBetweenElements = offsetFromPrevious / (next.element.getBoundingClientRect().top - previousBounds.top);
|
||||
const line = previous.line + progressBetweenElements * (next.line - previous.line);
|
||||
return clampLine(line);
|
||||
} else {
|
||||
return previous.line;
|
||||
const progressWithinElement = offsetFromPrevious / (previousBounds.height);
|
||||
const line = previous.line + progressWithinElement;
|
||||
return clampLine(line);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
class ActiveLineMarker {
|
||||
update(before) {
|
||||
onDidChangeTextEditorSelection(line) {
|
||||
const { previous } = getElementsForSourceLine(line);
|
||||
this._update(previous && previous.element);
|
||||
}
|
||||
|
||||
_update(before) {
|
||||
this._unmarkActiveElement(this._current);
|
||||
this._markActiveElement(before);
|
||||
this._current = before;
|
||||
@@ -142,21 +226,36 @@
|
||||
}
|
||||
|
||||
var scrollDisabled = true;
|
||||
var marker = new ActiveLineMarker();
|
||||
const marker = new ActiveLineMarker();
|
||||
const settings = JSON.parse(document.getElementById('vscode-markdown-preview-data').getAttribute('data-settings'));
|
||||
|
||||
function onLoad() {
|
||||
if (settings.scrollPreviewWithEditorSelection) {
|
||||
const initialLine = +settings.line;
|
||||
if (!isNaN(initialLine)) {
|
||||
setTimeout(() => {
|
||||
if (settings.scrollPreviewWithEditor) {
|
||||
setTimeout(() => {
|
||||
const initialLine = +settings.line;
|
||||
if (!isNaN(initialLine)) {
|
||||
scrollDisabled = true;
|
||||
scrollToRevealSourceLine(initialLine);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
const onUpdateView = (() => {
|
||||
const doScroll = throttle(line => {
|
||||
scrollDisabled = true;
|
||||
scrollToRevealSourceLine(line);
|
||||
}, 50);
|
||||
|
||||
return (line, settings) => {
|
||||
if (!isNaN(line)) {
|
||||
settings.line = line;
|
||||
doScroll(line);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
if (document.readyState === 'loading' || document.readyState === 'uninitialized') {
|
||||
document.addEventListener('DOMContentLoaded', onLoad);
|
||||
} else {
|
||||
@@ -168,18 +267,21 @@
|
||||
scrollDisabled = true;
|
||||
}, true);
|
||||
|
||||
window.addEventListener('message', (() => {
|
||||
const doScroll = throttle(line => {
|
||||
scrollDisabled = true;
|
||||
scrollToRevealSourceLine(line);
|
||||
}, 50);
|
||||
return event => {
|
||||
const line = +event.data.line;
|
||||
if (!isNaN(line)) {
|
||||
doScroll(line);
|
||||
}
|
||||
};
|
||||
})(), false);
|
||||
window.addEventListener('message', event => {
|
||||
if (event.data.source !== settings.source) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.data.type) {
|
||||
case 'onDidChangeTextEditorSelection':
|
||||
marker.onDidChangeTextEditorSelection(event.data.line);
|
||||
break;
|
||||
|
||||
case 'updateView':
|
||||
onUpdateView(event.data.line, settings);
|
||||
break;
|
||||
}
|
||||
}, false);
|
||||
|
||||
document.addEventListener('dblclick', event => {
|
||||
if (!settings.doubleClickToSwitchToEditor) {
|
||||
@@ -187,7 +289,7 @@
|
||||
}
|
||||
|
||||
// Ignore clicks on links
|
||||
for (let node = event.target; node; node = node.parentNode) {
|
||||
for (let node = /** @type {HTMLElement} */(event.target); node; node = /** @type {HTMLElement} */(node.parentNode)) {
|
||||
if (node.tagName === "A") {
|
||||
return;
|
||||
}
|
||||
@@ -196,14 +298,37 @@
|
||||
const offset = event.pageY;
|
||||
const line = getEditorLineNumberForPageOffset(offset);
|
||||
if (!isNaN(line)) {
|
||||
const args = [settings.source, line];
|
||||
window.parent.postMessage({
|
||||
command: "did-click-link",
|
||||
data: `command:_markdown.didClick?${encodeURIComponent(JSON.stringify(args))}`
|
||||
}, "file://");
|
||||
postMessage('didClick', { line });
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', event => {
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
const baseElement = document.getElementsByTagName('base')[0];
|
||||
|
||||
/** @type {*} */
|
||||
let node = event.target;
|
||||
while (node) {
|
||||
if (node.tagName && node.tagName === 'A' && node.href) {
|
||||
if (node.getAttribute('href').startsWith('#')) {
|
||||
break;
|
||||
}
|
||||
if (node.href.startsWith('file://') || node.href.startsWith('vscode-workspace-resource:')) {
|
||||
const [path, fragment] = node.href.replace(/^(file:\/\/|vscode-workspace-resource:)/i, '').split('#');
|
||||
postCommand('_markdown.openDocumentLink', [{ path, fragment }]);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
}, true);
|
||||
|
||||
if (settings.scrollEditorWithPreview) {
|
||||
window.addEventListener('scroll', throttle(() => {
|
||||
if (scrollDisabled) {
|
||||
@@ -211,13 +336,9 @@
|
||||
} else {
|
||||
const line = getEditorLineNumberForPageOffset(window.scrollY);
|
||||
if (!isNaN(line)) {
|
||||
const args = [settings.source, line];
|
||||
window.parent.postMessage({
|
||||
command: 'did-click-link',
|
||||
data: `command:_markdown.revealLine?${encodeURIComponent(JSON.stringify(args))}`
|
||||
}, 'file://');
|
||||
postMessage('revealLine', { line });
|
||||
}
|
||||
}
|
||||
}, 50));
|
||||
}
|
||||
}());
|
||||
}());
|
||||
|
||||
190
extensions/markdown/npm-shrinkwrap.json
generated
190
extensions/markdown/npm-shrinkwrap.json
generated
@@ -1,190 +0,0 @@
|
||||
{
|
||||
"name": "vscode-markdown",
|
||||
"version": "0.2.0",
|
||||
"dependencies": {
|
||||
"@types/highlight.js": {
|
||||
"version": "9.1.10",
|
||||
"from": "@types/highlight.js@9.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.1.10.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"@types/markdown-it": {
|
||||
"version": "0.0.2",
|
||||
"from": "@types/markdown-it@0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-0.0.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "7.0.43",
|
||||
"from": "@types/node@7.0.43",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.43.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"applicationinsights": {
|
||||
"version": "0.18.0",
|
||||
"from": "applicationinsights@https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz"
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.9",
|
||||
"from": "argparse@https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz"
|
||||
},
|
||||
"binaryextensions": {
|
||||
"version": "1.0.1",
|
||||
"from": "binaryextensions@1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-1.0.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"from": "core-util-is@1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"entities": {
|
||||
"version": "1.1.1",
|
||||
"from": "entities@https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz"
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"from": "escape-string-regexp@1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"gulp-rename": {
|
||||
"version": "1.2.2",
|
||||
"from": "gulp-rename@1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.2.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"gulp-replace": {
|
||||
"version": "0.5.4",
|
||||
"from": "gulp-replace@0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-0.5.4.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "9.5.0",
|
||||
"from": "highlight.js@https://registry.npmjs.org/highlight.js/-/highlight.js-9.5.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.5.0.tgz"
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"from": "inherits@2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"from": "isarray@1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"istextorbinary": {
|
||||
"version": "1.0.2",
|
||||
"from": "istextorbinary@1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-1.0.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"linkify-it": {
|
||||
"version": "2.0.3",
|
||||
"from": "linkify-it@https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz"
|
||||
},
|
||||
"markdown-it": {
|
||||
"version": "8.4.0",
|
||||
"from": "markdown-it@8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.0.tgz"
|
||||
},
|
||||
"markdown-it-named-headers": {
|
||||
"version": "0.0.4",
|
||||
"from": "markdown-it-named-headers@https://registry.npmjs.org/markdown-it-named-headers/-/markdown-it-named-headers-0.0.4.tgz",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-named-headers/-/markdown-it-named-headers-0.0.4.tgz"
|
||||
},
|
||||
"mdurl": {
|
||||
"version": "1.0.1",
|
||||
"from": "mdurl@https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz"
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"from": "object-assign@4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "1.0.7",
|
||||
"from": "process-nextick-args@1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.3",
|
||||
"from": "readable-stream@2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"replacestream": {
|
||||
"version": "4.0.2",
|
||||
"from": "replacestream@4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"from": "safe-buffer@5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"from": "sprintf-js@https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
|
||||
},
|
||||
"string": {
|
||||
"version": "3.3.1",
|
||||
"from": "string@https://registry.npmjs.org/string/-/string-3.3.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/string/-/string-3.3.1.tgz"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"from": "string_decoder@1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"textextensions": {
|
||||
"version": "1.0.2",
|
||||
"from": "textextensions@1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/textextensions/-/textextensions-1.0.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"uc.micro": {
|
||||
"version": "1.0.3",
|
||||
"from": "uc.micro@https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.3.tgz"
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"from": "util-deprecate@1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"vscode-extension-telemetry": {
|
||||
"version": "0.0.8",
|
||||
"from": "vscode-extension-telemetry@https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz",
|
||||
"resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz"
|
||||
},
|
||||
"vscode-nls": {
|
||||
"version": "2.0.2",
|
||||
"from": "vscode-nls@https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz",
|
||||
"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz"
|
||||
},
|
||||
"winreg": {
|
||||
"version": "1.2.3",
|
||||
"from": "winreg@https://registry.npmjs.org/winreg/-/winreg-1.2.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.3.tgz"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
{
|
||||
"name": "vscode-markdown",
|
||||
"displayName": "VS Code Markdown",
|
||||
"description": "Markdown for VS Code",
|
||||
"version": "0.2.0",
|
||||
"publisher": "Microsoft",
|
||||
"name": "markdown-language-features",
|
||||
"displayName": "%displayName%",
|
||||
"description": "%description%",
|
||||
"version": "1.0.0",
|
||||
"icon": "icon.png",
|
||||
"publisher": "vscode",
|
||||
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
|
||||
"enableProposedApi": true,
|
||||
"engines": {
|
||||
"vscode": "^1.0.0"
|
||||
"vscode": "^1.20.0"
|
||||
},
|
||||
"main": "./out/extension",
|
||||
"categories": [
|
||||
@@ -14,81 +16,15 @@
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:markdown",
|
||||
"onCommand:markdown.refreshPreview",
|
||||
"onCommand:markdown.preview.toggleLock",
|
||||
"onCommand:markdown.preview.refresh",
|
||||
"onCommand:markdown.showPreview",
|
||||
"onCommand:markdown.showPreviewToSide",
|
||||
"onCommand:markdown.showLockedPreviewToSide",
|
||||
"onCommand:markdown.showSource",
|
||||
"onCommand:markdown.showPreviewSecuritySelector"
|
||||
],
|
||||
"contributes": {
|
||||
"languages": [
|
||||
{
|
||||
"id": "markdown",
|
||||
"aliases": [
|
||||
"Markdown",
|
||||
"markdown"
|
||||
],
|
||||
"extensions": [
|
||||
".md",
|
||||
".mdown",
|
||||
".markdown",
|
||||
".markdn"
|
||||
],
|
||||
"configuration": "./language-configuration.json"
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
{
|
||||
"language": "markdown",
|
||||
"scopeName": "text.html.markdown",
|
||||
"path": "./syntaxes/markdown.tmLanguage",
|
||||
"embeddedLanguages": {
|
||||
"meta.embedded.block.html": "html",
|
||||
"source.js": "javascript",
|
||||
"source.css": "css",
|
||||
"meta.embedded.block.frontmatter": "yaml",
|
||||
"meta.embedded.block.css": "css",
|
||||
"meta.embedded.block.ini": "ini",
|
||||
"meta.embedded.block.java": "java",
|
||||
"meta.embedded.block.lua": "lua",
|
||||
"meta.embedded.block.makefile": "makefile",
|
||||
"meta.embedded.block.perl": "perl",
|
||||
"meta.embedded.block.r": "r",
|
||||
"meta.embedded.block.ruby": "ruby",
|
||||
"meta.embedded.block.php": "php",
|
||||
"meta.embedded.block.sql": "sql",
|
||||
"meta.embedded.block.vs_net": "vs_net",
|
||||
"meta.embedded.block.xml": "xml",
|
||||
"meta.embedded.block.xsl": "xsl",
|
||||
"meta.embedded.block.yaml": "yaml",
|
||||
"meta.embedded.block.dosbatch": "dosbatch",
|
||||
"meta.embedded.block.clojure": "clojure",
|
||||
"meta.embedded.block.coffee": "coffee",
|
||||
"meta.embedded.block.c": "c",
|
||||
"meta.embedded.block.cpp": "cpp",
|
||||
"meta.embedded.block.diff": "diff",
|
||||
"meta.embedded.block.dockerfile": "dockerfile",
|
||||
"meta.embedded.block.go": "go",
|
||||
"meta.embedded.block.groovy": "groovy",
|
||||
"meta.embedded.block.jade": "jade",
|
||||
"meta.embedded.block.javascript": "javascript",
|
||||
"meta.embedded.block.json": "json",
|
||||
"meta.embedded.block.less": "less",
|
||||
"meta.embedded.block.objc": "objc",
|
||||
"meta.embedded.block.scss": "scss",
|
||||
"meta.embedded.block.perl6": "perl6",
|
||||
"meta.embedded.block.powershell": "powershell",
|
||||
"meta.embedded.block.python": "python",
|
||||
"meta.embedded.block.rust": "rust",
|
||||
"meta.embedded.block.scala": "scala",
|
||||
"meta.embedded.block.shellscript": "shellscript",
|
||||
"meta.embedded.block.typescript": "typescript",
|
||||
"meta.embedded.block.typescriptreact": "typescriptreact",
|
||||
"meta.embedded.block.csharp": "csharp",
|
||||
"meta.embedded.block.fsharp": "fsharp"
|
||||
}
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"command": "markdown.showPreview",
|
||||
@@ -108,6 +44,15 @@
|
||||
"dark": "./media/PreviewOnRightPane_16x_dark.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "markdown.showLockedPreviewToSide",
|
||||
"title": "%markdown.showLockedPreviewToSide.title%",
|
||||
"category": "Markdown",
|
||||
"icon": {
|
||||
"light": "./media/PreviewOnRightPane_16x.svg",
|
||||
"dark": "./media/PreviewOnRightPane_16x_dark.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "markdown.showSource",
|
||||
"title": "%markdown.showSource.title%",
|
||||
@@ -118,13 +63,18 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "markdown.refreshPreview",
|
||||
"title": "%markdown.refreshPreview.title%",
|
||||
"command": "markdown.showPreviewSecuritySelector",
|
||||
"title": "%markdown.showPreviewSecuritySelector.title%",
|
||||
"category": "Markdown"
|
||||
},
|
||||
{
|
||||
"command": "markdown.showPreviewSecuritySelector",
|
||||
"title": "%markdown.showPreviewSecuritySelector.title%",
|
||||
"command": "markdown.preview.refresh",
|
||||
"title": "%markdown.preview.refresh.title%",
|
||||
"category": "Markdown"
|
||||
},
|
||||
{
|
||||
"command": "markdown.preview.toggleLock",
|
||||
"title": "%markdown.preview.toggleLock.title%",
|
||||
"category": "Markdown"
|
||||
}
|
||||
],
|
||||
@@ -138,16 +88,23 @@
|
||||
},
|
||||
{
|
||||
"command": "markdown.showSource",
|
||||
"when": "resourceScheme == markdown",
|
||||
"when": "markdownPreviewFocus",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "markdown.refreshPreview",
|
||||
"when": "resourceScheme == markdown"
|
||||
"command": "markdown.preview.refresh",
|
||||
"when": "markdownPreviewFocus",
|
||||
"group": "1_markdown"
|
||||
},
|
||||
{
|
||||
"command": "markdown.preview.toggleLock",
|
||||
"when": "markdownPreviewFocus",
|
||||
"group": "1_markdown"
|
||||
},
|
||||
{
|
||||
"command": "markdown.showPreviewSecuritySelector",
|
||||
"when": "resourceScheme == markdown"
|
||||
"when": "markdownPreviewFocus",
|
||||
"group": "1_markdown"
|
||||
}
|
||||
],
|
||||
"explorer/context": [
|
||||
@@ -168,9 +125,14 @@
|
||||
"when": "editorLangId == markdown",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "markdown.showLockedPreviewToSide",
|
||||
"when": "editorLangId == markdown",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "markdown.showSource",
|
||||
"when": "resourceScheme == markdown",
|
||||
"when": "markdownPreviewFocus",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
@@ -179,7 +141,11 @@
|
||||
},
|
||||
{
|
||||
"command": "markdown.showPreviewSecuritySelector",
|
||||
"when": "resourceScheme == markdown"
|
||||
"when": "markdownPreviewFocus"
|
||||
},
|
||||
{
|
||||
"command": "markdown.preview.toggleLock",
|
||||
"when": "markdownPreviewFocus"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -197,12 +163,6 @@
|
||||
"when": "editorLangId == markdown"
|
||||
}
|
||||
],
|
||||
"snippets": [
|
||||
{
|
||||
"language": "markdown",
|
||||
"path": "./snippets/markdown.json"
|
||||
}
|
||||
],
|
||||
"configuration": {
|
||||
"type": "object",
|
||||
"title": "Markdown",
|
||||
@@ -254,11 +214,17 @@
|
||||
"description": "%markdown.preview.lineHeight.desc%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"markdown.preview.scrollPreviewWithEditor": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "%markdown.preview.scrollPreviewWithEditor.desc%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"markdown.preview.scrollPreviewWithEditorSelection": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "%markdown.preview.scrollPreviewWithEditorSelection.desc%",
|
||||
"scope": "resource"
|
||||
"deprecationMessage": "%markdown.preview.scrollPreviewWithEditorSelection.deprecationMessage%"
|
||||
},
|
||||
"markdown.preview.markEditorSelection": {
|
||||
"type": "boolean",
|
||||
@@ -304,21 +270,19 @@
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown ./tsconfig.json",
|
||||
"update-grammar": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ./syntaxes/gulpfile.js"
|
||||
"vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown ./tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"highlight.js": "9.5.0",
|
||||
"markdown-it": "^8.4.0",
|
||||
"markdown-it-named-headers": "0.0.4",
|
||||
"vscode-extension-telemetry": "0.0.8",
|
||||
"vscode-nls": "2.0.2"
|
||||
"vscode-extension-telemetry": "0.0.15",
|
||||
"vscode-nls": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/highlight.js": "9.1.10",
|
||||
"@types/markdown-it": "0.0.2",
|
||||
"@types/node": "7.0.43",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-replace": "^0.5.4"
|
||||
"vscode": "^1.1.10"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"displayName": "Markdown Language Features",
|
||||
"description": "Provides rich language support for Markdown.",
|
||||
"markdown.preview.breaks.desc": "Sets how line-breaks are rendered in the markdown preview. Setting it to 'true' creates a <br> for every newline.",
|
||||
"markdown.preview.linkify": "Enable or disable conversion of URL-like text to links in the markdown preview.",
|
||||
"markdown.preview.doubleClickToSwitchToEditor.desc": "Double click in the markdown preview to switch to the editor.",
|
||||
@@ -6,14 +8,18 @@
|
||||
"markdown.preview.fontSize.desc": "Controls the font size in pixels used in the markdown preview.",
|
||||
"markdown.preview.lineHeight.desc": "Controls the line height used in the markdown preview. This number is relative to the font size.",
|
||||
"markdown.preview.markEditorSelection.desc": "Mark the current editor selection in the markdown preview.",
|
||||
"markdown.preview.scrollEditorWithPreview.desc": "When the markdown preview is scrolled, update the view of the editor.",
|
||||
"markdown.preview.scrollPreviewWithEditorSelection.desc": "Scrolls the markdown preview to reveal the currently selected line from the editor.",
|
||||
"markdown.preview.scrollEditorWithPreview.desc": "When a markdown preview is scrolled, update the view of the editor.",
|
||||
"markdown.preview.scrollPreviewWithEditor.desc": "When a markdown editor is scrolled, update the view of the preview.",
|
||||
"markdown.preview.scrollPreviewWithEditorSelection.desc": "[Deprecated] Scrolls the markdown preview to reveal the currently selected line from the editor.",
|
||||
"markdown.preview.scrollPreviewWithEditorSelection.deprecationMessage": "This setting has been replaced by 'markdown.preview.scrollPreviewWithEditor' and no longer has any effect.",
|
||||
"markdown.preview.title" : "Open Preview",
|
||||
"markdown.previewFrontMatter.dec": "Sets how YAML front matter should be rendered in the markdown preview. 'hide' removes the front matter. Otherwise, the front matter is treated as markdown content.",
|
||||
"markdown.previewSide.title" : "Open Preview to the Side",
|
||||
"markdown.showLockedPreviewToSide.title": "Open Locked Preview to the Side",
|
||||
"markdown.showSource.title" : "Show Source",
|
||||
"markdown.styles.dec": "A list of URLs or local paths to CSS style sheets to use from the markdown preview. Relative paths are interpreted relative to the folder open in the explorer. If there is no open folder, they are interpreted relative to the location of the markdown file. All '\\' need to be written as '\\\\'.",
|
||||
"markdown.showPreviewSecuritySelector.title": "Change Preview Security Settings",
|
||||
"markdown.trace.desc": "Enable debug logging for the markdown extension.",
|
||||
"markdown.refreshPreview.title": "Refresh Preview"
|
||||
}
|
||||
"markdown.preview.refresh.title": "Refresh Preview",
|
||||
"markdown.preview.toggleLock.title": "Toggle Preview Locking"
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
{
|
||||
"Insert bold text": {
|
||||
"prefix": "bold",
|
||||
"body": "**${1:${TM_SELECTED_TEXT}}**$0",
|
||||
"description": "Insert bold text"
|
||||
},
|
||||
"Insert italic text": {
|
||||
"prefix": "italic",
|
||||
"body": "*${1:${TM_SELECTED_TEXT}}*$0",
|
||||
"description": "Insert italic text"
|
||||
},
|
||||
"Insert quoted text": {
|
||||
"prefix": "quote",
|
||||
"body": "> ${1:${TM_SELECTED_TEXT}}",
|
||||
"description": "Insert quoted text"
|
||||
},
|
||||
"Insert code": {
|
||||
"prefix": "code",
|
||||
"body": "`${1:${TM_SELECTED_TEXT}}`$0",
|
||||
"description": "Insert code"
|
||||
},
|
||||
"Insert fenced code block": {
|
||||
"prefix": "fenced codeblock",
|
||||
"body": [
|
||||
"```${1:language}",
|
||||
"$0",
|
||||
"```"
|
||||
],
|
||||
"description": "Insert fenced code block"
|
||||
},
|
||||
"Insert heading": {
|
||||
"prefix": "heading",
|
||||
"body": "# ${1:text}",
|
||||
"description": "Insert heading"
|
||||
},
|
||||
"Insert unordered list": {
|
||||
"prefix": "unordered list",
|
||||
"body": [
|
||||
"- ${1:first}",
|
||||
"- ${2:second}",
|
||||
"- ${3:third}",
|
||||
"$0"
|
||||
],
|
||||
"description": "Insert unordered list"
|
||||
},
|
||||
"Insert ordered list": {
|
||||
"prefix": "ordered list",
|
||||
"body": [
|
||||
"1. ${1:first}",
|
||||
"2. ${2:second}",
|
||||
"3. ${3:third}",
|
||||
"$0"
|
||||
],
|
||||
"description": "Insert ordered list"
|
||||
},
|
||||
"Insert horizontal rule": {
|
||||
"prefix": "horizontal rule",
|
||||
"body": "----------\n",
|
||||
"description": "Insert horizontal rule"
|
||||
},
|
||||
"Insert link": {
|
||||
"prefix": "link",
|
||||
"body": "[${1:text}](http://${2:link})$0",
|
||||
"description": "Insert link"
|
||||
},
|
||||
"Insert image": {
|
||||
"prefix": "image",
|
||||
"body": "$0",
|
||||
"description": "Insert image"
|
||||
}
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.config(process.env.VSCODE_NLS_CONFIG)();
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
|
||||
import { Command } from './commandManager';
|
||||
import { ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './security';
|
||||
import { getMarkdownUri, MDDocumentContentProvider, isMarkdownFile } from './features/previewContentProvider';
|
||||
import { Logger } from './logger';
|
||||
import { TableOfContentsProvider } from './tableOfContentsProvider';
|
||||
import { MarkdownEngine } from './markdownEngine';
|
||||
import { TelemetryReporter } from './telemetryReporter';
|
||||
|
||||
|
||||
function getViewColumn(sideBySide: boolean): vscode.ViewColumn | undefined {
|
||||
const active = vscode.window.activeTextEditor;
|
||||
if (!active) {
|
||||
return vscode.ViewColumn.One;
|
||||
}
|
||||
|
||||
if (!sideBySide) {
|
||||
return active.viewColumn;
|
||||
}
|
||||
|
||||
switch (active.viewColumn) {
|
||||
case vscode.ViewColumn.One:
|
||||
return vscode.ViewColumn.Two;
|
||||
case vscode.ViewColumn.Two:
|
||||
return vscode.ViewColumn.Three;
|
||||
}
|
||||
|
||||
return active.viewColumn;
|
||||
}
|
||||
|
||||
function showPreview(
|
||||
cspArbiter: ExtensionContentSecurityPolicyArbiter,
|
||||
telemetryReporter: TelemetryReporter,
|
||||
uri?: vscode.Uri,
|
||||
sideBySide: boolean = false,
|
||||
) {
|
||||
let resource = uri;
|
||||
if (!(resource instanceof vscode.Uri)) {
|
||||
if (vscode.window.activeTextEditor) {
|
||||
// we are relaxed and don't check for markdown files
|
||||
resource = vscode.window.activeTextEditor.document.uri;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(resource instanceof vscode.Uri)) {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
// this is most likely toggling the preview
|
||||
return vscode.commands.executeCommand('markdown.showSource');
|
||||
}
|
||||
// nothing found that could be shown or toggled
|
||||
return;
|
||||
}
|
||||
|
||||
const thenable = vscode.commands.executeCommand('vscode.previewHtml',
|
||||
getMarkdownUri(resource),
|
||||
getViewColumn(sideBySide),
|
||||
localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath)),
|
||||
{
|
||||
allowScripts: true,
|
||||
allowSvgs: cspArbiter.shouldAllowSvgsForResource(resource)
|
||||
});
|
||||
|
||||
telemetryReporter.sendTelemetryEvent('openPreview', {
|
||||
where: sideBySide ? 'sideBySide' : 'inPlace',
|
||||
how: (uri instanceof vscode.Uri) ? 'action' : 'pallete'
|
||||
});
|
||||
|
||||
return thenable;
|
||||
}
|
||||
|
||||
export class ShowPreviewCommand implements Command {
|
||||
public readonly id = 'markdown.showPreview';
|
||||
|
||||
public constructor(
|
||||
private readonly cspArbiter: ExtensionContentSecurityPolicyArbiter,
|
||||
private readonly telemetryReporter: TelemetryReporter
|
||||
) { }
|
||||
|
||||
public execute(uri?: vscode.Uri) {
|
||||
showPreview(this.cspArbiter, this.telemetryReporter, uri, false);
|
||||
}
|
||||
}
|
||||
|
||||
export class ShowPreviewToSideCommand implements Command {
|
||||
public readonly id = 'markdown.showPreviewToSide';
|
||||
|
||||
public constructor(
|
||||
private readonly cspArbiter: ExtensionContentSecurityPolicyArbiter,
|
||||
private readonly telemetryReporter: TelemetryReporter
|
||||
) { }
|
||||
|
||||
public execute(uri?: vscode.Uri) {
|
||||
showPreview(this.cspArbiter, this.telemetryReporter, uri, true);
|
||||
}
|
||||
}
|
||||
|
||||
export class ShowSourceCommand implements Command {
|
||||
public readonly id = 'markdown.showSource';
|
||||
|
||||
public execute(mdUri?: vscode.Uri) {
|
||||
if (!mdUri) {
|
||||
return vscode.commands.executeCommand('workbench.action.navigateBack');
|
||||
}
|
||||
|
||||
const docUri = vscode.Uri.parse(mdUri.query);
|
||||
for (const editor of vscode.window.visibleTextEditors) {
|
||||
if (editor.document.uri.scheme === docUri.scheme && editor.document.uri.toString() === docUri.toString()) {
|
||||
return vscode.window.showTextDocument(editor.document, editor.viewColumn);
|
||||
}
|
||||
}
|
||||
|
||||
return vscode.workspace.openTextDocument(docUri)
|
||||
.then(vscode.window.showTextDocument);
|
||||
}
|
||||
}
|
||||
|
||||
export class RefreshPreviewCommand implements Command {
|
||||
public readonly id = 'markdown.refreshPreview';
|
||||
|
||||
public constructor(
|
||||
private readonly contentProvider: MDDocumentContentProvider
|
||||
) { }
|
||||
|
||||
public execute(resource: string | undefined) {
|
||||
if (resource) {
|
||||
const source = vscode.Uri.parse(resource);
|
||||
this.contentProvider.update(source);
|
||||
} else if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) {
|
||||
this.contentProvider.update(getMarkdownUri(vscode.window.activeTextEditor.document.uri));
|
||||
} else {
|
||||
// update all generated md documents
|
||||
for (const document of vscode.workspace.textDocuments) {
|
||||
if (document.uri.scheme === 'markdown') {
|
||||
this.contentProvider.update(document.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ShowPreviewSecuritySelectorCommand implements Command {
|
||||
public readonly id = 'markdown.showPreviewSecuritySelector';
|
||||
|
||||
public constructor(
|
||||
private readonly previewSecuritySelector: PreviewSecuritySelector
|
||||
) { }
|
||||
|
||||
public execute(resource: string | undefined) {
|
||||
if (resource) {
|
||||
const source = vscode.Uri.parse(resource).query;
|
||||
this.previewSecuritySelector.showSecutitySelectorForResource(vscode.Uri.parse(source));
|
||||
} else {
|
||||
if (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document.languageId === 'markdown') {
|
||||
this.previewSecuritySelector.showSecutitySelectorForResource(vscode.window.activeTextEditor.document.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class RevealLineCommand implements Command {
|
||||
public readonly id = '_markdown.revealLine';
|
||||
|
||||
public constructor(
|
||||
private logger: Logger
|
||||
) { }
|
||||
|
||||
public execute(uri: string, line: number) {
|
||||
const sourceUri = vscode.Uri.parse(decodeURIComponent(uri));
|
||||
this.logger.log('revealLine', { uri, sourceUri: sourceUri.toString(), line });
|
||||
|
||||
vscode.window.visibleTextEditors
|
||||
.filter(editor => isMarkdownFile(editor.document) && editor.document.uri.toString() === sourceUri.toString())
|
||||
.forEach(editor => {
|
||||
const sourceLine = Math.floor(line);
|
||||
const fraction = line - sourceLine;
|
||||
const text = editor.document.lineAt(sourceLine).text;
|
||||
const start = Math.floor(fraction * text.length);
|
||||
editor.revealRange(
|
||||
new vscode.Range(sourceLine, start, sourceLine + 1, 0),
|
||||
vscode.TextEditorRevealType.AtTop);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class DidClickCommand implements Command {
|
||||
public readonly id = '_markdown.didClick';
|
||||
|
||||
public execute(uri: string, line: number) {
|
||||
const sourceUri = vscode.Uri.parse(decodeURIComponent(uri));
|
||||
return vscode.workspace.openTextDocument(sourceUri)
|
||||
.then(document => vscode.window.showTextDocument(document))
|
||||
.then(editor =>
|
||||
vscode.commands.executeCommand('revealLine', { lineNumber: Math.floor(line), at: 'center' })
|
||||
.then(() => editor))
|
||||
.then(editor => {
|
||||
if (editor) {
|
||||
editor.selection = new vscode.Selection(
|
||||
new vscode.Position(Math.floor(line), 0),
|
||||
new vscode.Position(Math.floor(line), 0));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class MoveCursorToPositionCommand implements Command {
|
||||
public readonly id = '_markdown.moveCursorToPosition';
|
||||
|
||||
public execute(line: number, character: number) {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
const position = new vscode.Position(line, character);
|
||||
const selection = new vscode.Selection(position, position);
|
||||
vscode.window.activeTextEditor.revealRange(selection);
|
||||
vscode.window.activeTextEditor.selection = selection;
|
||||
}
|
||||
}
|
||||
|
||||
export class OnPreviewStyleLoadErrorCommand implements Command {
|
||||
public readonly id = '_markdown.onPreviewStyleLoadError';
|
||||
|
||||
public execute(resources: string[]) {
|
||||
vscode.window.showWarningMessage(localize('onPreviewStyleLoadError', "Could not load 'markdown.styles': {0}", resources.join(', ')));
|
||||
}
|
||||
}
|
||||
|
||||
export interface OpenDocumentLinkArgs {
|
||||
path: string;
|
||||
fragment: string;
|
||||
}
|
||||
|
||||
export class OpenDocumentLinkCommand implements Command {
|
||||
private static readonly id = '_markdown.openDocumentLink';
|
||||
public readonly id = OpenDocumentLinkCommand.id;
|
||||
|
||||
public static createCommandUri(
|
||||
path: string,
|
||||
fragment: string
|
||||
): vscode.Uri {
|
||||
return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify({ path, fragment }))}`);
|
||||
}
|
||||
|
||||
public constructor(
|
||||
private readonly engine: MarkdownEngine
|
||||
) { }
|
||||
|
||||
public execute(args: OpenDocumentLinkArgs) {
|
||||
const tryRevealLine = async (editor: vscode.TextEditor) => {
|
||||
if (editor && args.fragment) {
|
||||
const toc = new TableOfContentsProvider(this.engine, editor.document);
|
||||
const line = await toc.lookup(args.fragment);
|
||||
if (!isNaN(line)) {
|
||||
return editor.revealRange(
|
||||
new vscode.Range(line, 0, line, 0),
|
||||
vscode.TextEditorRevealType.AtTop);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const tryOpen = async (path: string) => {
|
||||
if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document) && vscode.window.activeTextEditor.document.uri.fsPath === path) {
|
||||
return tryRevealLine(vscode.window.activeTextEditor);
|
||||
} else {
|
||||
const resource = vscode.Uri.file(path);
|
||||
return vscode.workspace.openTextDocument(resource)
|
||||
.then(vscode.window.showTextDocument)
|
||||
.then(tryRevealLine);
|
||||
}
|
||||
};
|
||||
|
||||
return tryOpen(args.path).catch(() => {
|
||||
if (path.extname(args.path) === '') {
|
||||
return tryOpen(args.path + '.md');
|
||||
}
|
||||
const resource = vscode.Uri.file(args.path);
|
||||
return Promise.resolve(void 0)
|
||||
.then(() => vscode.commands.executeCommand('vscode.open', resource))
|
||||
.then(() => void 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
13
extensions/markdown/src/commands/index.ts
Normal file
13
extensions/markdown/src/commands/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export { OpenDocumentLinkCommand } from './openDocumentLink';
|
||||
export { OnPreviewStyleLoadErrorCommand } from './onPreviewStyleLoadError';
|
||||
export { ShowPreviewCommand, ShowPreviewToSideCommand, ShowLockedPreviewToSideCommand } from './showPreview';
|
||||
export { ShowSourceCommand } from './showSource';
|
||||
export { RefreshPreviewCommand } from './refreshPreview';
|
||||
export { ShowPreviewSecuritySelectorCommand } from './showPreviewSecuritySelector';
|
||||
export { MoveCursorToPositionCommand } from './moveCursorToPosition';
|
||||
export { ToggleLockCommand } from './toggleLock';
|
||||
22
extensions/markdown/src/commands/moveCursorToPosition.ts
Normal file
22
extensions/markdown/src/commands/moveCursorToPosition.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { Command } from '../commandManager';
|
||||
|
||||
export class MoveCursorToPositionCommand implements Command {
|
||||
public readonly id = '_markdown.moveCursorToPosition';
|
||||
|
||||
public execute(line: number, character: number) {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
const position = new vscode.Position(line, character);
|
||||
const selection = new vscode.Selection(position, position);
|
||||
vscode.window.activeTextEditor.revealRange(selection);
|
||||
vscode.window.activeTextEditor.selection = selection;
|
||||
}
|
||||
}
|
||||
18
extensions/markdown/src/commands/onPreviewStyleLoadError.ts
Normal file
18
extensions/markdown/src/commands/onPreviewStyleLoadError.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { Command } from '../commandManager';
|
||||
|
||||
export class OnPreviewStyleLoadErrorCommand implements Command {
|
||||
public readonly id = '_markdown.onPreviewStyleLoadError';
|
||||
|
||||
public execute(resources: string[]) {
|
||||
vscode.window.showWarningMessage(localize('onPreviewStyleLoadError', "Could not load 'markdown.styles': {0}", resources.join(', ')));
|
||||
}
|
||||
}
|
||||
74
extensions/markdown/src/commands/openDocumentLink.ts
Normal file
74
extensions/markdown/src/commands/openDocumentLink.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
|
||||
import { Command } from '../commandManager';
|
||||
import { MarkdownEngine } from '../markdownEngine';
|
||||
import { TableOfContentsProvider } from '../tableOfContentsProvider';
|
||||
import { isMarkdownFile } from '../util/file';
|
||||
|
||||
|
||||
export interface OpenDocumentLinkArgs {
|
||||
path: string;
|
||||
fragment: string;
|
||||
}
|
||||
|
||||
export class OpenDocumentLinkCommand implements Command {
|
||||
private static readonly id = '_markdown.openDocumentLink';
|
||||
public readonly id = OpenDocumentLinkCommand.id;
|
||||
|
||||
public static createCommandUri(
|
||||
path: string,
|
||||
fragment: string
|
||||
): vscode.Uri {
|
||||
return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify({ path, fragment }))}`);
|
||||
}
|
||||
|
||||
public constructor(
|
||||
private readonly engine: MarkdownEngine
|
||||
) { }
|
||||
|
||||
public execute(args: OpenDocumentLinkArgs) {
|
||||
return this.tryOpen(args.path, args).catch(() => {
|
||||
if (path.extname(args.path) === '') {
|
||||
return this.tryOpen(args.path + '.md', args);
|
||||
}
|
||||
const resource = vscode.Uri.file(args.path);
|
||||
return Promise.resolve(void 0)
|
||||
.then(() => vscode.commands.executeCommand('vscode.open', resource))
|
||||
.then(() => void 0);
|
||||
});
|
||||
}
|
||||
|
||||
private async tryOpen(path: string, args: OpenDocumentLinkArgs) {
|
||||
if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document) && vscode.window.activeTextEditor.document.uri.fsPath === path) {
|
||||
return this.tryRevealLine(vscode.window.activeTextEditor, args.fragment);
|
||||
} else {
|
||||
const resource = vscode.Uri.file(path);
|
||||
return vscode.workspace.openTextDocument(resource)
|
||||
.then(vscode.window.showTextDocument)
|
||||
.then(editor => this.tryRevealLine(editor, args.fragment));
|
||||
}
|
||||
}
|
||||
|
||||
private async tryRevealLine(editor: vscode.TextEditor, fragment?: string) {
|
||||
if (editor && fragment) {
|
||||
const toc = new TableOfContentsProvider(this.engine, editor.document);
|
||||
const entry = await toc.lookup(fragment);
|
||||
if (entry) {
|
||||
return editor.revealRange(new vscode.Range(entry.line, 0, entry.line, 0), vscode.TextEditorRevealType.AtTop);
|
||||
}
|
||||
const lineNumberFragment = fragment.match(/^L(\d+)$/);
|
||||
if (lineNumberFragment) {
|
||||
const line = +lineNumberFragment[1] - 1;
|
||||
if (!isNaN(line)) {
|
||||
return editor.revealRange(new vscode.Range(line, 0, line, 0), vscode.TextEditorRevealType.AtTop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
extensions/markdown/src/commands/refreshPreview.ts
Normal file
19
extensions/markdown/src/commands/refreshPreview.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Command } from '../commandManager';
|
||||
import { MarkdownPreviewManager } from '../features/previewManager';
|
||||
|
||||
export class RefreshPreviewCommand implements Command {
|
||||
public readonly id = 'markdown.preview.refresh';
|
||||
|
||||
public constructor(
|
||||
private readonly webviewManager: MarkdownPreviewManager
|
||||
) { }
|
||||
|
||||
public execute() {
|
||||
this.webviewManager.refresh();
|
||||
}
|
||||
}
|
||||
123
extensions/markdown/src/commands/showPreview.ts
Normal file
123
extensions/markdown/src/commands/showPreview.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { Command } from '../commandManager';
|
||||
import { MarkdownPreviewManager } from '../features/previewManager';
|
||||
import { TelemetryReporter } from '../telemetryReporter';
|
||||
import { PreviewSettings } from '../features/preview';
|
||||
|
||||
|
||||
function getViewColumn(sideBySide: boolean): vscode.ViewColumn | undefined {
|
||||
const active = vscode.window.activeTextEditor;
|
||||
if (!active) {
|
||||
return vscode.ViewColumn.One;
|
||||
}
|
||||
|
||||
if (!sideBySide) {
|
||||
return active.viewColumn;
|
||||
}
|
||||
|
||||
switch (active.viewColumn) {
|
||||
case vscode.ViewColumn.One:
|
||||
return vscode.ViewColumn.Two;
|
||||
case vscode.ViewColumn.Two:
|
||||
return vscode.ViewColumn.Three;
|
||||
}
|
||||
|
||||
return active.viewColumn;
|
||||
}
|
||||
|
||||
interface ShowPreviewSettings {
|
||||
readonly sideBySide?: boolean;
|
||||
readonly locked?: boolean;
|
||||
}
|
||||
|
||||
async function showPreview(
|
||||
webviewManager: MarkdownPreviewManager,
|
||||
telemetryReporter: TelemetryReporter,
|
||||
uri: vscode.Uri | undefined,
|
||||
previewSettings: ShowPreviewSettings,
|
||||
): Promise<any> {
|
||||
let resource = uri;
|
||||
if (!(resource instanceof vscode.Uri)) {
|
||||
if (vscode.window.activeTextEditor) {
|
||||
// we are relaxed and don't check for markdown files
|
||||
resource = vscode.window.activeTextEditor.document.uri;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(resource instanceof vscode.Uri)) {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
// this is most likely toggling the preview
|
||||
return vscode.commands.executeCommand('markdown.showSource');
|
||||
}
|
||||
// nothing found that could be shown or toggled
|
||||
return;
|
||||
}
|
||||
|
||||
webviewManager.preview(resource, {
|
||||
resourceColumn: (vscode.window.activeTextEditor && vscode.window.activeTextEditor.viewColumn) || vscode.ViewColumn.One,
|
||||
previewColumn: getViewColumn(!!previewSettings.sideBySide) || vscode.ViewColumn.Active,
|
||||
locked: !!previewSettings.locked
|
||||
});
|
||||
|
||||
telemetryReporter.sendTelemetryEvent('openPreview', {
|
||||
where: previewSettings.sideBySide ? 'sideBySide' : 'inPlace',
|
||||
how: (uri instanceof vscode.Uri) ? 'action' : 'pallete'
|
||||
});
|
||||
}
|
||||
|
||||
export class ShowPreviewCommand implements Command {
|
||||
public readonly id = 'markdown.showPreview';
|
||||
|
||||
public constructor(
|
||||
private readonly webviewManager: MarkdownPreviewManager,
|
||||
private readonly telemetryReporter: TelemetryReporter
|
||||
) { }
|
||||
|
||||
public execute(mainUri?: vscode.Uri, allUris?: vscode.Uri[], previewSettings?: PreviewSettings) {
|
||||
for (const uri of (allUris || [mainUri])) {
|
||||
showPreview(this.webviewManager, this.telemetryReporter, uri, {
|
||||
sideBySide: false,
|
||||
locked: previewSettings && previewSettings.locked
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ShowPreviewToSideCommand implements Command {
|
||||
public readonly id = 'markdown.showPreviewToSide';
|
||||
|
||||
public constructor(
|
||||
private readonly webviewManager: MarkdownPreviewManager,
|
||||
private readonly telemetryReporter: TelemetryReporter
|
||||
) { }
|
||||
|
||||
public execute(uri?: vscode.Uri, previewSettings?: PreviewSettings) {
|
||||
showPreview(this.webviewManager, this.telemetryReporter, uri, {
|
||||
sideBySide: true,
|
||||
locked: previewSettings && previewSettings.locked
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ShowLockedPreviewToSideCommand implements Command {
|
||||
public readonly id = 'markdown.showLockedPreviewToSide';
|
||||
|
||||
public constructor(
|
||||
private readonly webviewManager: MarkdownPreviewManager,
|
||||
private readonly telemetryReporter: TelemetryReporter
|
||||
) { }
|
||||
|
||||
public execute(uri?: vscode.Uri) {
|
||||
showPreview(this.webviewManager, this.telemetryReporter, uri, {
|
||||
sideBySide: true,
|
||||
locked: true
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Command } from '../commandManager';
|
||||
import { PreviewSecuritySelector } from '../security';
|
||||
import { isMarkdownFile } from '../util/file';
|
||||
import { MarkdownPreviewManager } from '../features/previewManager';
|
||||
|
||||
export class ShowPreviewSecuritySelectorCommand implements Command {
|
||||
public readonly id = 'markdown.showPreviewSecuritySelector';
|
||||
|
||||
public constructor(
|
||||
private readonly previewSecuritySelector: PreviewSecuritySelector,
|
||||
private readonly previewManager: MarkdownPreviewManager
|
||||
) { }
|
||||
|
||||
public execute(resource: string | undefined) {
|
||||
if (resource) {
|
||||
const source = vscode.Uri.parse(resource).query;
|
||||
this.previewSecuritySelector.showSecutitySelectorForResource(vscode.Uri.parse(source));
|
||||
} else if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) {
|
||||
this.previewSecuritySelector.showSecutitySelectorForResource(vscode.window.activeTextEditor.document.uri);
|
||||
} else if (this.previewManager.activePreviewResource) {
|
||||
this.previewSecuritySelector.showSecutitySelectorForResource(this.previewManager.activePreviewResource);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
extensions/markdown/src/commands/showSource.ts
Normal file
30
extensions/markdown/src/commands/showSource.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Command } from '../commandManager';
|
||||
import { MarkdownPreviewManager } from '../features/previewManager';
|
||||
|
||||
export class ShowSourceCommand implements Command {
|
||||
public readonly id = 'markdown.showSource';
|
||||
|
||||
public constructor(
|
||||
private readonly previewManager: MarkdownPreviewManager
|
||||
) { }
|
||||
|
||||
|
||||
public execute(docUri?: vscode.Uri) {
|
||||
if (!docUri) {
|
||||
return vscode.commands.executeCommand('workbench.action.navigateBack');
|
||||
}
|
||||
|
||||
const resource = this.previewManager.getResourceForPreview(docUri);
|
||||
if (resource) {
|
||||
return vscode.workspace.openTextDocument(resource)
|
||||
.then(document => vscode.window.showTextDocument(document));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
20
extensions/markdown/src/commands/toggleLock.ts
Normal file
20
extensions/markdown/src/commands/toggleLock.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Command } from '../commandManager';
|
||||
import { MarkdownPreviewManager } from '../features/previewManager';
|
||||
|
||||
export class ToggleLockCommand implements Command {
|
||||
public readonly id = 'markdown.preview.toggleLock';
|
||||
|
||||
public constructor(
|
||||
private readonly previewManager: MarkdownPreviewManager
|
||||
) { }
|
||||
|
||||
public execute(previewUri?: vscode.Uri) {
|
||||
this.previewManager.toggleLock(previewUri);
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
|
||||
function normalizeLink(document: vscode.TextDocument, link: string, base: string): vscode.Uri {
|
||||
const uri = vscode.Uri.parse(link);
|
||||
if (uri.scheme) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
// assume it must be a file
|
||||
let resourcePath = uri.path;
|
||||
if (!uri.path) {
|
||||
resourcePath = document.uri.path;
|
||||
} else if (uri.path[0] === '/') {
|
||||
const root = vscode.workspace.getWorkspaceFolder(document.uri);
|
||||
if (root) {
|
||||
resourcePath = path.join(root.uri.fsPath, uri.path);
|
||||
}
|
||||
} else {
|
||||
resourcePath = path.join(base, uri.path);
|
||||
}
|
||||
|
||||
return vscode.Uri.parse(`command:_markdown.openDocumentLink?${encodeURIComponent(JSON.stringify({ fragment: uri.fragment, path: resourcePath }))}`);
|
||||
}
|
||||
|
||||
function matchAll(pattern: RegExp, text: string): Array<RegExpMatchArray> {
|
||||
const out: RegExpMatchArray[] = [];
|
||||
pattern.lastIndex = 0;
|
||||
let match: RegExpMatchArray | null;
|
||||
while ((match = pattern.exec(text))) {
|
||||
out.push(match);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export default class LinkProvider implements vscode.DocumentLinkProvider {
|
||||
private linkPattern = /(\[[^\]]*\]\(\s*?)(((((?=.*\)\)+)|(?=.*\)\]+))[^\s\)]+?)|([^\s]+)))\)/g;
|
||||
private referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g;
|
||||
private definitionPattern = /^([\t ]*\[([^\]]+)\]:\s*)(\S+)/gm;
|
||||
|
||||
public provideDocumentLinks(
|
||||
document: vscode.TextDocument,
|
||||
_token: vscode.CancellationToken
|
||||
): vscode.DocumentLink[] {
|
||||
const base = path.dirname(document.uri.fsPath);
|
||||
const text = document.getText();
|
||||
|
||||
return this.providerInlineLinks(text, document, base)
|
||||
.concat(this.provideReferenceLinks(text, document, base));
|
||||
}
|
||||
|
||||
private providerInlineLinks(
|
||||
text: string,
|
||||
document: vscode.TextDocument,
|
||||
base: string
|
||||
): vscode.DocumentLink[] {
|
||||
const results: vscode.DocumentLink[] = [];
|
||||
for (const match of matchAll(this.linkPattern, text)) {
|
||||
const pre = match[1];
|
||||
const link = match[2];
|
||||
const offset = (match.index || 0) + pre.length;
|
||||
const linkStart = document.positionAt(offset);
|
||||
const linkEnd = document.positionAt(offset + link.length);
|
||||
try {
|
||||
results.push(new vscode.DocumentLink(
|
||||
new vscode.Range(linkStart, linkEnd),
|
||||
normalizeLink(document, link, base)));
|
||||
} catch (e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private provideReferenceLinks(
|
||||
text: string,
|
||||
document: vscode.TextDocument,
|
||||
base: string
|
||||
): vscode.DocumentLink[] {
|
||||
const results: vscode.DocumentLink[] = [];
|
||||
|
||||
const definitions = this.getDefinitions(text, document);
|
||||
for (const match of matchAll(this.referenceLinkPattern, text)) {
|
||||
let linkStart: vscode.Position;
|
||||
let linkEnd: vscode.Position;
|
||||
let reference = match[3];
|
||||
if (reference) { // [text][ref]
|
||||
const pre = match[1];
|
||||
const offset = (match.index || 0) + pre.length;
|
||||
linkStart = document.positionAt(offset);
|
||||
linkEnd = document.positionAt(offset + reference.length);
|
||||
} else if (match[2]) { // [ref][]
|
||||
reference = match[2];
|
||||
const offset = (match.index || 0) + 1;
|
||||
linkStart = document.positionAt(offset);
|
||||
linkEnd = document.positionAt(offset + match[2].length);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const link = definitions.get(reference);
|
||||
if (link) {
|
||||
results.push(new vscode.DocumentLink(
|
||||
new vscode.Range(linkStart, linkEnd),
|
||||
vscode.Uri.parse(`command:_markdown.moveCursorToPosition?${encodeURIComponent(JSON.stringify([link.linkRange.start.line, link.linkRange.start.character]))}`)));
|
||||
}
|
||||
} catch (e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
for (const definition of Array.from(definitions.values())) {
|
||||
try {
|
||||
results.push(new vscode.DocumentLink(
|
||||
definition.linkRange,
|
||||
normalizeLink(document, definition.link, base)));
|
||||
} catch (e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private getDefinitions(text: string, document: vscode.TextDocument) {
|
||||
const out = new Map<string, { link: string, linkRange: vscode.Range }>();
|
||||
for (const match of matchAll(this.definitionPattern, text)) {
|
||||
const pre = match[1];
|
||||
const reference = match[2];
|
||||
const link = match[3].trim();
|
||||
|
||||
const offset = (match.index || 0) + pre.length;
|
||||
const linkStart = document.positionAt(offset);
|
||||
const linkEnd = document.positionAt(offset + link.length);
|
||||
|
||||
out.set(reference, {
|
||||
link: link,
|
||||
linkRange: new vscode.Range(linkStart, linkEnd)
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { MarkdownEngine } from './markdownEngine';
|
||||
import { TableOfContentsProvider } from './tableOfContentsProvider';
|
||||
|
||||
export default class MDDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
|
||||
|
||||
constructor(
|
||||
private engine: MarkdownEngine
|
||||
) { }
|
||||
|
||||
public async provideDocumentSymbols(document: vscode.TextDocument): Promise<vscode.SymbolInformation[]> {
|
||||
const toc = await new TableOfContentsProvider(this.engine, document).getToc();
|
||||
return toc.map(entry => {
|
||||
return new vscode.SymbolInformation('#'.repeat(entry.level) + ' ' + entry.text, vscode.SymbolKind.Namespace, '', entry.location);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,13 @@ import { MarkdownEngine } from './markdownEngine';
|
||||
import { ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './security';
|
||||
import { Logger } from './logger';
|
||||
import { CommandManager } from './commandManager';
|
||||
import * as commands from './commands';
|
||||
import * as commands from './commands/index';
|
||||
import { loadDefaultTelemetryReporter } from './telemetryReporter';
|
||||
import { loadMarkdownExtensions } from './markdownExtensions';
|
||||
import LinkProvider from './features/documentLinkProvider';
|
||||
import MDDocumentSymbolProvider from './features/documentSymbolProvider';
|
||||
import { MDDocumentContentProvider, getMarkdownUri, isMarkdownFile } from './features/previewContentProvider';
|
||||
import { MarkdownContentProvider } from './features/previewContentProvider';
|
||||
import { MarkdownPreviewManager } from './features/previewManager';
|
||||
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
@@ -27,58 +28,32 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
const selector = 'markdown';
|
||||
|
||||
const contentProvider = new MDDocumentContentProvider(engine, context, cspArbiter, logger);
|
||||
context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(selector, contentProvider));
|
||||
|
||||
const contentProvider = new MarkdownContentProvider(engine, context, cspArbiter, logger);
|
||||
loadMarkdownExtensions(contentProvider, engine);
|
||||
|
||||
const previewManager = new MarkdownPreviewManager(contentProvider, logger);
|
||||
context.subscriptions.push(previewManager);
|
||||
|
||||
context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider(selector, new MDDocumentSymbolProvider(engine)));
|
||||
context.subscriptions.push(vscode.languages.registerDocumentLinkProvider(selector, new LinkProvider()));
|
||||
|
||||
const previewSecuritySelector = new PreviewSecuritySelector(cspArbiter, contentProvider);
|
||||
const previewSecuritySelector = new PreviewSecuritySelector(cspArbiter, previewManager);
|
||||
|
||||
const commandManager = new CommandManager();
|
||||
context.subscriptions.push(commandManager);
|
||||
commandManager.register(new commands.ShowPreviewCommand(cspArbiter, telemetryReporter));
|
||||
commandManager.register(new commands.ShowPreviewToSideCommand(cspArbiter, telemetryReporter));
|
||||
commandManager.register(new commands.ShowSourceCommand());
|
||||
commandManager.register(new commands.RefreshPreviewCommand(contentProvider));
|
||||
commandManager.register(new commands.RevealLineCommand(logger));
|
||||
commandManager.register(new commands.ShowPreviewCommand(previewManager, telemetryReporter));
|
||||
commandManager.register(new commands.ShowPreviewToSideCommand(previewManager, telemetryReporter));
|
||||
commandManager.register(new commands.ShowLockedPreviewToSideCommand(previewManager, telemetryReporter));
|
||||
commandManager.register(new commands.ShowSourceCommand(previewManager));
|
||||
commandManager.register(new commands.RefreshPreviewCommand(previewManager));
|
||||
commandManager.register(new commands.MoveCursorToPositionCommand());
|
||||
commandManager.register(new commands.ShowPreviewSecuritySelectorCommand(previewSecuritySelector));
|
||||
commandManager.register(new commands.ShowPreviewSecuritySelectorCommand(previewSecuritySelector, previewManager));
|
||||
commandManager.register(new commands.OnPreviewStyleLoadErrorCommand());
|
||||
commandManager.register(new commands.DidClickCommand());
|
||||
commandManager.register(new commands.OpenDocumentLinkCommand(engine));
|
||||
|
||||
context.subscriptions.push(vscode.workspace.onDidSaveTextDocument(document => {
|
||||
if (isMarkdownFile(document)) {
|
||||
const uri = getMarkdownUri(document.uri);
|
||||
contentProvider.update(uri);
|
||||
}
|
||||
}));
|
||||
|
||||
context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(event => {
|
||||
if (isMarkdownFile(event.document)) {
|
||||
const uri = getMarkdownUri(event.document.uri);
|
||||
contentProvider.update(uri);
|
||||
}
|
||||
}));
|
||||
commandManager.register(new commands.ToggleLockCommand(previewManager));
|
||||
|
||||
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => {
|
||||
logger.updateConfiguration();
|
||||
contentProvider.updateConfiguration();
|
||||
}));
|
||||
|
||||
context.subscriptions.push(vscode.window.onDidChangeTextEditorSelection(event => {
|
||||
if (isMarkdownFile(event.textEditor.document)) {
|
||||
const markdownFile = getMarkdownUri(event.textEditor.document.uri);
|
||||
logger.log('updatePreviewForSelection', { markdownFile: markdownFile.toString() });
|
||||
|
||||
vscode.commands.executeCommand('_workbench.htmlPreview.postMessage',
|
||||
markdownFile,
|
||||
{
|
||||
line: event.selections[0].active.line
|
||||
});
|
||||
}
|
||||
previewManager.updateConfiguration();
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import { OpenDocumentLinkCommand } from '../commands';
|
||||
import { OpenDocumentLinkCommand } from '../commands/openDocumentLink';
|
||||
|
||||
function normalizeLink(
|
||||
document: vscode.TextDocument,
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { MarkdownEngine } from '../markdownEngine';
|
||||
@@ -13,7 +11,7 @@ import { TableOfContentsProvider } from '../tableOfContentsProvider';
|
||||
export default class MDDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
|
||||
|
||||
constructor(
|
||||
private engine: MarkdownEngine
|
||||
private readonly engine: MarkdownEngine
|
||||
) { }
|
||||
|
||||
public async provideDocumentSymbols(document: vscode.TextDocument): Promise<vscode.SymbolInformation[]> {
|
||||
|
||||
299
extensions/markdown/src/features/preview.ts
Normal file
299
extensions/markdown/src/features/preview.ts
Normal file
@@ -0,0 +1,299 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
|
||||
import { Logger } from '../logger';
|
||||
import { MarkdownContentProvider } from './previewContentProvider';
|
||||
import { disposeAll } from '../util/dispose';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import { getVisibleLine, MarkdownFileTopmostLineMonitor } from '../util/topmostLineMonitor';
|
||||
import { MarkdownPreviewConfigurationManager } from './previewConfig';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class MarkdownPreview {
|
||||
|
||||
public static previewScheme = 'vscode-markdown-preview';
|
||||
private static previewCount = 0;
|
||||
|
||||
public readonly uri: vscode.Uri;
|
||||
private readonly webview: vscode.Webview;
|
||||
private throttleTimer: any;
|
||||
private initialLine: number | undefined = undefined;
|
||||
private readonly disposables: vscode.Disposable[] = [];
|
||||
private firstUpdate = true;
|
||||
private currentVersion?: { resource: vscode.Uri, version: number };
|
||||
private forceUpdate = false;
|
||||
private isScrolling = false;
|
||||
|
||||
constructor(
|
||||
private _resource: vscode.Uri,
|
||||
previewColumn: vscode.ViewColumn,
|
||||
public locked: boolean,
|
||||
private readonly contentProvider: MarkdownContentProvider,
|
||||
private readonly previewConfigurations: MarkdownPreviewConfigurationManager,
|
||||
private readonly logger: Logger,
|
||||
topmostLineMonitor: MarkdownFileTopmostLineMonitor
|
||||
) {
|
||||
this.uri = vscode.Uri.parse(`${MarkdownPreview.previewScheme}:${MarkdownPreview.previewCount++}`);
|
||||
this.webview = vscode.window.createWebview(
|
||||
this.uri,
|
||||
this.getPreviewTitle(this._resource),
|
||||
previewColumn, {
|
||||
enableScripts: true,
|
||||
enableCommandUris: true,
|
||||
localResourceRoots: this.getLocalResourceRoots(_resource)
|
||||
});
|
||||
|
||||
this.webview.onDidDispose(() => {
|
||||
this.dispose();
|
||||
}, null, this.disposables);
|
||||
|
||||
this.webview.onDidChangeViewColumn(() => {
|
||||
this._onDidChangeViewColumnEmitter.fire();
|
||||
}, null, this.disposables);
|
||||
|
||||
this.webview.onDidReceiveMessage(e => {
|
||||
if (e.source !== this._resource.toString()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (e.type) {
|
||||
case 'command':
|
||||
vscode.commands.executeCommand(e.body.command, ...e.body.args);
|
||||
break;
|
||||
|
||||
case 'revealLine':
|
||||
this.onDidScrollPreview(e.body.line);
|
||||
break;
|
||||
|
||||
case 'didClick':
|
||||
this.onDidClickPreview(e.body.line);
|
||||
break;
|
||||
|
||||
}
|
||||
}, null, this.disposables);
|
||||
|
||||
vscode.workspace.onDidChangeTextDocument(event => {
|
||||
if (this.isPreviewOf(event.document.uri)) {
|
||||
this.refresh();
|
||||
}
|
||||
}, null, this.disposables);
|
||||
|
||||
topmostLineMonitor.onDidChangeTopmostLine(event => {
|
||||
if (this.isPreviewOf(event.resource)) {
|
||||
this.updateForView(event.resource, event.line);
|
||||
}
|
||||
}, null, this.disposables);
|
||||
|
||||
vscode.window.onDidChangeTextEditorSelection(event => {
|
||||
if (this.isPreviewOf(event.textEditor.document.uri)) {
|
||||
this.webview.postMessage({
|
||||
type: 'onDidChangeTextEditorSelection',
|
||||
line: event.selections[0].active.line,
|
||||
source: this.resource.toString()
|
||||
});
|
||||
}
|
||||
}, null, this.disposables);
|
||||
}
|
||||
|
||||
private readonly _onDisposeEmitter = new vscode.EventEmitter<void>();
|
||||
public readonly onDispose = this._onDisposeEmitter.event;
|
||||
|
||||
private readonly _onDidChangeViewColumnEmitter = new vscode.EventEmitter<vscode.ViewColumn>();
|
||||
public readonly onDidChangeViewColumn = this._onDidChangeViewColumnEmitter.event;
|
||||
|
||||
public get resource(): vscode.Uri {
|
||||
return this._resource;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this._onDisposeEmitter.fire();
|
||||
|
||||
this._onDisposeEmitter.dispose();
|
||||
this._onDidChangeViewColumnEmitter.dispose();
|
||||
this.webview.dispose();
|
||||
|
||||
disposeAll(this.disposables);
|
||||
}
|
||||
|
||||
public update(resource: vscode.Uri) {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (editor && editor.document.uri.fsPath === resource.fsPath) {
|
||||
this.initialLine = getVisibleLine(editor);
|
||||
} else {
|
||||
this.initialLine = undefined;
|
||||
}
|
||||
|
||||
// If we have changed resources, cancel any pending updates
|
||||
const isResourceChange = resource.fsPath !== this._resource.fsPath;
|
||||
if (isResourceChange) {
|
||||
clearTimeout(this.throttleTimer);
|
||||
this.throttleTimer = undefined;
|
||||
}
|
||||
|
||||
this._resource = resource;
|
||||
|
||||
// Schedule update if none is pending
|
||||
if (!this.throttleTimer) {
|
||||
if (isResourceChange || this.firstUpdate) {
|
||||
this.doUpdate();
|
||||
} else {
|
||||
this.throttleTimer = setTimeout(() => this.doUpdate(), 300);
|
||||
}
|
||||
}
|
||||
|
||||
this.firstUpdate = false;
|
||||
}
|
||||
|
||||
public refresh() {
|
||||
this.forceUpdate = true;
|
||||
this.update(this._resource);
|
||||
}
|
||||
|
||||
public updateConfiguration() {
|
||||
if (this.previewConfigurations.hasConfigurationChanged(this._resource)) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public get viewColumn(): vscode.ViewColumn | undefined {
|
||||
return this.webview.viewColumn;
|
||||
}
|
||||
|
||||
public isPreviewOf(resource: vscode.Uri): boolean {
|
||||
return this._resource.fsPath === resource.fsPath;
|
||||
}
|
||||
|
||||
public matchesResource(
|
||||
otherResource: vscode.Uri,
|
||||
otherViewColumn: vscode.ViewColumn | undefined,
|
||||
otherLocked: boolean
|
||||
): boolean {
|
||||
if (this.viewColumn !== otherViewColumn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.locked) {
|
||||
return otherLocked && this.isPreviewOf(otherResource);
|
||||
} else {
|
||||
return !otherLocked;
|
||||
}
|
||||
}
|
||||
|
||||
public matches(otherPreview: MarkdownPreview): boolean {
|
||||
return this.matchesResource(otherPreview._resource, otherPreview.viewColumn, otherPreview.locked);
|
||||
}
|
||||
|
||||
public show(viewColumn: vscode.ViewColumn) {
|
||||
this.webview.show(viewColumn);
|
||||
}
|
||||
|
||||
public toggleLock() {
|
||||
this.locked = !this.locked;
|
||||
this.webview.title = this.getPreviewTitle(this._resource);
|
||||
}
|
||||
|
||||
private getPreviewTitle(resource: vscode.Uri): string {
|
||||
return this.locked
|
||||
? localize('lockedPreviewTitle', '[Preview] {0}', path.basename(resource.fsPath))
|
||||
: localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath));
|
||||
}
|
||||
|
||||
private updateForView(resource: vscode.Uri, topLine: number | undefined) {
|
||||
if (!this.isPreviewOf(resource)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isScrolling) {
|
||||
this.isScrolling = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof topLine === 'number') {
|
||||
this.logger.log('updateForView', { markdownFile: resource });
|
||||
this.initialLine = topLine;
|
||||
this.webview.postMessage({
|
||||
type: 'updateView',
|
||||
line: topLine,
|
||||
source: resource.toString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async doUpdate(): Promise<void> {
|
||||
const resource = this._resource;
|
||||
|
||||
clearTimeout(this.throttleTimer);
|
||||
this.throttleTimer = undefined;
|
||||
|
||||
const document = await vscode.workspace.openTextDocument(resource);
|
||||
if (!this.forceUpdate && this.currentVersion && this.currentVersion.resource.fsPath === resource.fsPath && this.currentVersion.version === document.version) {
|
||||
if (this.initialLine) {
|
||||
this.updateForView(resource, this.initialLine);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.forceUpdate = false;
|
||||
|
||||
this.currentVersion = { resource, version: document.version };
|
||||
this.contentProvider.provideTextDocumentContent(document, this.previewConfigurations, this.initialLine)
|
||||
.then(content => {
|
||||
if (this._resource === resource) {
|
||||
this.webview.title = this.getPreviewTitle(this._resource);
|
||||
this.webview.html = content;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getLocalResourceRoots(resource: vscode.Uri): vscode.Uri[] {
|
||||
const folder = vscode.workspace.getWorkspaceFolder(resource);
|
||||
if (folder) {
|
||||
return [folder.uri];
|
||||
}
|
||||
|
||||
if (!resource.scheme || resource.scheme === 'file') {
|
||||
return [vscode.Uri.file(path.dirname(resource.fsPath))];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private onDidScrollPreview(line: number) {
|
||||
for (const editor of vscode.window.visibleTextEditors) {
|
||||
if (!this.isPreviewOf(editor.document.uri)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.isScrolling = true;
|
||||
const sourceLine = Math.floor(line);
|
||||
const fraction = line - sourceLine;
|
||||
const text = editor.document.lineAt(sourceLine).text;
|
||||
const start = Math.floor(fraction * text.length);
|
||||
editor.revealRange(
|
||||
new vscode.Range(sourceLine, start, sourceLine + 1, 0),
|
||||
vscode.TextEditorRevealType.AtTop);
|
||||
}
|
||||
}
|
||||
|
||||
private async onDidClickPreview(line: number): Promise<void> {
|
||||
for (const visibleEditor of vscode.window.visibleTextEditors) {
|
||||
if (this.isPreviewOf(visibleEditor.document.uri)) {
|
||||
const editor = await vscode.window.showTextDocument(visibleEditor.document, visibleEditor.viewColumn);
|
||||
const position = new vscode.Position(line, 0);
|
||||
editor.selection = new vscode.Selection(position, position);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface PreviewSettings {
|
||||
readonly resourceColumn: vscode.ViewColumn;
|
||||
readonly previewColumn: vscode.ViewColumn;
|
||||
readonly locked: boolean;
|
||||
}
|
||||
104
extensions/markdown/src/features/previewConfig.ts
Normal file
104
extensions/markdown/src/features/previewConfig.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export class MarkdownPreviewConfiguration {
|
||||
public static getForResource(resource: vscode.Uri) {
|
||||
return new MarkdownPreviewConfiguration(resource);
|
||||
}
|
||||
|
||||
public readonly scrollBeyondLastLine: boolean;
|
||||
public readonly wordWrap: boolean;
|
||||
public readonly previewFrontMatter: string;
|
||||
public readonly lineBreaks: boolean;
|
||||
public readonly doubleClickToSwitchToEditor: boolean;
|
||||
public readonly scrollEditorWithPreview: boolean;
|
||||
public readonly scrollPreviewWithEditor: boolean;
|
||||
public readonly markEditorSelection: boolean;
|
||||
|
||||
public readonly lineHeight: number;
|
||||
public readonly fontSize: number;
|
||||
public readonly fontFamily: string | undefined;
|
||||
public readonly styles: string[];
|
||||
|
||||
private constructor(resource: vscode.Uri) {
|
||||
const editorConfig = vscode.workspace.getConfiguration('editor', resource);
|
||||
const markdownConfig = vscode.workspace.getConfiguration('markdown', resource);
|
||||
const markdownEditorConfig = vscode.workspace.getConfiguration('[markdown]');
|
||||
|
||||
this.scrollBeyondLastLine = editorConfig.get<boolean>('scrollBeyondLastLine', false);
|
||||
|
||||
this.wordWrap = editorConfig.get<string>('wordWrap', 'off') !== 'off';
|
||||
if (markdownEditorConfig && markdownEditorConfig['editor.wordWrap']) {
|
||||
this.wordWrap = markdownEditorConfig['editor.wordWrap'] !== 'off';
|
||||
}
|
||||
|
||||
this.previewFrontMatter = markdownConfig.get<string>('previewFrontMatter', 'hide');
|
||||
this.scrollPreviewWithEditor = !!markdownConfig.get<boolean>('preview.scrollPreviewWithEditor', true);
|
||||
this.scrollEditorWithPreview = !!markdownConfig.get<boolean>('preview.scrollEditorWithPreview', true);
|
||||
this.lineBreaks = !!markdownConfig.get<boolean>('preview.breaks', false);
|
||||
this.doubleClickToSwitchToEditor = !!markdownConfig.get<boolean>('preview.doubleClickToSwitchToEditor', true);
|
||||
this.markEditorSelection = !!markdownConfig.get<boolean>('preview.markEditorSelection', true);
|
||||
|
||||
this.fontFamily = markdownConfig.get<string | undefined>('preview.fontFamily', undefined);
|
||||
this.fontSize = Math.max(8, +markdownConfig.get<number>('preview.fontSize', NaN));
|
||||
this.lineHeight = Math.max(0.6, +markdownConfig.get<number>('preview.lineHeight', NaN));
|
||||
|
||||
this.styles = markdownConfig.get<string[]>('styles', []);
|
||||
}
|
||||
|
||||
public isEqualTo(otherConfig: MarkdownPreviewConfiguration) {
|
||||
for (let key in this) {
|
||||
if (this.hasOwnProperty(key) && key !== 'styles') {
|
||||
if (this[key] !== otherConfig[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check styles
|
||||
if (this.styles.length !== otherConfig.styles.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < this.styles.length; ++i) {
|
||||
if (this.styles[i] !== otherConfig.styles[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export class MarkdownPreviewConfigurationManager {
|
||||
private readonly previewConfigurationsForWorkspaces = new Map<string, MarkdownPreviewConfiguration>();
|
||||
|
||||
public loadAndCacheConfiguration(
|
||||
resource: vscode.Uri
|
||||
): MarkdownPreviewConfiguration {
|
||||
const config = MarkdownPreviewConfiguration.getForResource(resource);
|
||||
this.previewConfigurationsForWorkspaces.set(this.getKey(resource), config);
|
||||
return config;
|
||||
}
|
||||
|
||||
public hasConfigurationChanged(
|
||||
resource: vscode.Uri
|
||||
): boolean {
|
||||
const key = this.getKey(resource);
|
||||
const currentConfig = this.previewConfigurationsForWorkspaces.get(key);
|
||||
const newConfig = MarkdownPreviewConfiguration.getForResource(resource);
|
||||
return (!currentConfig || !currentConfig.isEqualTo(newConfig));
|
||||
}
|
||||
|
||||
private getKey(
|
||||
resource: vscode.Uri
|
||||
): string {
|
||||
const folder = vscode.workspace.getWorkspaceFolder(resource);
|
||||
return folder ? folder.uri.toString() : '';
|
||||
}
|
||||
}
|
||||
@@ -3,154 +3,46 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import { MarkdownEngine } from '../markdownEngine';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import { Logger } from '../logger';
|
||||
import { ContentSecurityPolicyArbiter, MarkdownPreviewSecurityLevel } from '../security';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import { Logger } from '../logger';
|
||||
import { ContentSecurityPolicyArbiter, MarkdownPreviewSecurityLevel } from '../security';
|
||||
import { MarkdownPreviewConfigurationManager, MarkdownPreviewConfiguration } from './previewConfig';
|
||||
|
||||
/**
|
||||
* Strings used inside the markdown preview.
|
||||
*
|
||||
* Stored here and then injected in the preview so that they
|
||||
* can be localized using our normal localization process.
|
||||
*/
|
||||
const previewStrings = {
|
||||
cspAlertMessageText: localize('preview.securityMessage.text', 'Some content has been disabled in this document'),
|
||||
cspAlertMessageTitle: localize('preview.securityMessage.title', 'Potentially unsafe or insecure content has been disabled in the markdown preview. Change the Markdown preview security setting to allow insecure content or enable scripts'),
|
||||
cspAlertMessageLabel: localize('preview.securityMessage.label', 'Content Disabled Security Warning')
|
||||
cspAlertMessageText: localize(
|
||||
'preview.securityMessage.text',
|
||||
'Some content has been disabled in this document'),
|
||||
|
||||
cspAlertMessageTitle: localize(
|
||||
'preview.securityMessage.title',
|
||||
'Potentially unsafe or insecure content has been disabled in the markdown preview. Change the Markdown preview security setting to allow insecure content or enable scripts'),
|
||||
|
||||
cspAlertMessageLabel: localize(
|
||||
'preview.securityMessage.label',
|
||||
'Content Disabled Security Warning')
|
||||
};
|
||||
|
||||
export function isMarkdownFile(document: vscode.TextDocument) {
|
||||
return document.languageId === 'markdown'
|
||||
&& document.uri.scheme !== 'markdown'; // prevent processing of own documents
|
||||
}
|
||||
|
||||
export function getMarkdownUri(uri: vscode.Uri) {
|
||||
if (uri.scheme === 'markdown') {
|
||||
return uri;
|
||||
}
|
||||
|
||||
return uri.with({
|
||||
scheme: 'markdown',
|
||||
path: uri.path + '.rendered',
|
||||
query: uri.toString()
|
||||
});
|
||||
}
|
||||
|
||||
class MarkdownPreviewConfig {
|
||||
public static getConfigForResource(resource: vscode.Uri) {
|
||||
return new MarkdownPreviewConfig(resource);
|
||||
}
|
||||
|
||||
public readonly scrollBeyondLastLine: boolean;
|
||||
public readonly wordWrap: boolean;
|
||||
public readonly previewFrontMatter: string;
|
||||
public readonly lineBreaks: boolean;
|
||||
public readonly doubleClickToSwitchToEditor: boolean;
|
||||
public readonly scrollEditorWithPreview: boolean;
|
||||
public readonly scrollPreviewWithEditorSelection: boolean;
|
||||
public readonly markEditorSelection: boolean;
|
||||
|
||||
public readonly lineHeight: number;
|
||||
public readonly fontSize: number;
|
||||
public readonly fontFamily: string | undefined;
|
||||
public readonly styles: string[];
|
||||
|
||||
private constructor(resource: vscode.Uri) {
|
||||
const editorConfig = vscode.workspace.getConfiguration('editor', resource);
|
||||
const markdownConfig = vscode.workspace.getConfiguration('markdown', resource);
|
||||
const markdownEditorConfig = vscode.workspace.getConfiguration('[markdown]');
|
||||
|
||||
this.scrollBeyondLastLine = editorConfig.get<boolean>('scrollBeyondLastLine', false);
|
||||
|
||||
this.wordWrap = editorConfig.get<string>('wordWrap', 'off') !== 'off';
|
||||
if (markdownEditorConfig && markdownEditorConfig['editor.wordWrap']) {
|
||||
this.wordWrap = markdownEditorConfig['editor.wordWrap'] !== 'off';
|
||||
}
|
||||
|
||||
this.previewFrontMatter = markdownConfig.get<string>('previewFrontMatter', 'hide');
|
||||
this.scrollPreviewWithEditorSelection = !!markdownConfig.get<boolean>('preview.scrollPreviewWithEditorSelection', true);
|
||||
this.scrollEditorWithPreview = !!markdownConfig.get<boolean>('preview.scrollEditorWithPreview', true);
|
||||
this.lineBreaks = !!markdownConfig.get<boolean>('preview.breaks', false);
|
||||
this.doubleClickToSwitchToEditor = !!markdownConfig.get<boolean>('preview.doubleClickToSwitchToEditor', true);
|
||||
this.markEditorSelection = !!markdownConfig.get<boolean>('preview.markEditorSelection', true);
|
||||
|
||||
this.fontFamily = markdownConfig.get<string | undefined>('preview.fontFamily', undefined);
|
||||
this.fontSize = Math.max(8, +markdownConfig.get<number>('preview.fontSize', NaN));
|
||||
this.lineHeight = Math.max(0.6, +markdownConfig.get<number>('preview.lineHeight', NaN));
|
||||
|
||||
this.styles = markdownConfig.get<string[]>('styles', []);
|
||||
}
|
||||
|
||||
public isEqualTo(otherConfig: MarkdownPreviewConfig) {
|
||||
for (let key in this) {
|
||||
if (this.hasOwnProperty(key) && key !== 'styles') {
|
||||
if (this[key] !== otherConfig[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check styles
|
||||
if (this.styles.length !== otherConfig.styles.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < this.styles.length; ++i) {
|
||||
if (this.styles[i] !== otherConfig.styles[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
class PreviewConfigManager {
|
||||
private previewConfigurationsForWorkspaces = new Map<string, MarkdownPreviewConfig>();
|
||||
|
||||
public loadAndCacheConfiguration(
|
||||
resource: vscode.Uri
|
||||
) {
|
||||
const config = MarkdownPreviewConfig.getConfigForResource(resource);
|
||||
this.previewConfigurationsForWorkspaces.set(this.getKey(resource), config);
|
||||
return config;
|
||||
}
|
||||
|
||||
public shouldUpdateConfiguration(
|
||||
resource: vscode.Uri
|
||||
): boolean {
|
||||
const key = this.getKey(resource);
|
||||
const currentConfig = this.previewConfigurationsForWorkspaces.get(key);
|
||||
const newConfig = MarkdownPreviewConfig.getConfigForResource(resource);
|
||||
return (!currentConfig || !currentConfig.isEqualTo(newConfig));
|
||||
}
|
||||
|
||||
private getKey(
|
||||
resource: vscode.Uri
|
||||
): string {
|
||||
const folder = vscode.workspace.getWorkspaceFolder(resource);
|
||||
if (!folder) {
|
||||
return '';
|
||||
}
|
||||
return folder.uri.toString();
|
||||
}
|
||||
}
|
||||
|
||||
export class MDDocumentContentProvider implements vscode.TextDocumentContentProvider {
|
||||
private _onDidChange = new vscode.EventEmitter<vscode.Uri>();
|
||||
private _waiting: boolean = false;
|
||||
private previewConfigurations = new PreviewConfigManager();
|
||||
|
||||
private extraStyles: Array<vscode.Uri> = [];
|
||||
private extraScripts: Array<vscode.Uri> = [];
|
||||
export class MarkdownContentProvider {
|
||||
private readonly extraStyles: Array<vscode.Uri> = [];
|
||||
private readonly extraScripts: Array<vscode.Uri> = [];
|
||||
|
||||
constructor(
|
||||
private engine: MarkdownEngine,
|
||||
private context: vscode.ExtensionContext,
|
||||
private cspArbiter: ContentSecurityPolicyArbiter,
|
||||
private logger: Logger
|
||||
private readonly engine: MarkdownEngine,
|
||||
private readonly context: vscode.ExtensionContext,
|
||||
private readonly cspArbiter: ContentSecurityPolicyArbiter,
|
||||
private readonly logger: Logger
|
||||
) { }
|
||||
|
||||
public addScript(resource: vscode.Uri): void {
|
||||
@@ -161,90 +53,18 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
|
||||
this.extraStyles.push(resource);
|
||||
}
|
||||
|
||||
private getMediaPath(mediaFile: string): string {
|
||||
return vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))).toString();
|
||||
}
|
||||
|
||||
private fixHref(resource: vscode.Uri, href: string): string {
|
||||
if (!href) {
|
||||
return href;
|
||||
}
|
||||
|
||||
// Use href if it is already an URL
|
||||
const hrefUri = vscode.Uri.parse(href);
|
||||
if (['file', 'http', 'https'].indexOf(hrefUri.scheme) >= 0) {
|
||||
return hrefUri.toString();
|
||||
}
|
||||
|
||||
// Use href as file URI if it is absolute
|
||||
if (path.isAbsolute(href)) {
|
||||
return vscode.Uri.file(href).toString();
|
||||
}
|
||||
|
||||
// use a workspace relative path if there is a workspace
|
||||
let root = vscode.workspace.getWorkspaceFolder(resource);
|
||||
if (root) {
|
||||
return vscode.Uri.file(path.join(root.uri.fsPath, href)).toString();
|
||||
}
|
||||
|
||||
// otherwise look relative to the markdown file
|
||||
return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)).toString();
|
||||
}
|
||||
|
||||
private computeCustomStyleSheetIncludes(resource: vscode.Uri, config: MarkdownPreviewConfig): string {
|
||||
if (config.styles && Array.isArray(config.styles)) {
|
||||
return config.styles.map(style => {
|
||||
return `<link rel="stylesheet" class="code-user-style" data-source="${style.replace(/"/g, '"')}" href="${this.fixHref(resource, style)}" type="text/css" media="screen">`;
|
||||
}).join('\n');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private getSettingsOverrideStyles(nonce: string, config: MarkdownPreviewConfig): string {
|
||||
return `<style nonce="${nonce}">
|
||||
body {
|
||||
${config.fontFamily ? `font-family: ${config.fontFamily};` : ''}
|
||||
${isNaN(config.fontSize) ? '' : `font-size: ${config.fontSize}px;`}
|
||||
${isNaN(config.lineHeight) ? '' : `line-height: ${config.lineHeight};`}
|
||||
}
|
||||
</style>`;
|
||||
}
|
||||
|
||||
private getStyles(resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfig): string {
|
||||
const baseStyles = [
|
||||
this.getMediaPath('markdown.css'),
|
||||
this.getMediaPath('tomorrow.css')
|
||||
].concat(this.extraStyles.map(resource => resource.toString()));
|
||||
|
||||
return `${baseStyles.map(href => `<link rel="stylesheet" type="text/css" href="${href}">`).join('\n')}
|
||||
${this.getSettingsOverrideStyles(nonce, config)}
|
||||
${this.computeCustomStyleSheetIncludes(resource, config)}`;
|
||||
}
|
||||
|
||||
private getScripts(nonce: string): string {
|
||||
const scripts = [this.getMediaPath('main.js')].concat(this.extraScripts.map(resource => resource.toString()));
|
||||
return scripts
|
||||
.map(source => `<script async src="${source}" nonce="${nonce}" charset="UTF-8"></script>`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
public async provideTextDocumentContent(uri: vscode.Uri): Promise<string> {
|
||||
const sourceUri = vscode.Uri.parse(uri.query);
|
||||
|
||||
let initialLine: number | undefined = undefined;
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (editor && editor.document.uri.toString() === sourceUri.toString()) {
|
||||
initialLine = editor.selection.active.line;
|
||||
}
|
||||
|
||||
const document = await vscode.workspace.openTextDocument(sourceUri);
|
||||
const config = this.previewConfigurations.loadAndCacheConfiguration(sourceUri);
|
||||
|
||||
public async provideTextDocumentContent(
|
||||
markdownDocument: vscode.TextDocument,
|
||||
previewConfigurations: MarkdownPreviewConfigurationManager,
|
||||
initialLine: number | undefined = undefined
|
||||
): Promise<string> {
|
||||
const sourceUri = markdownDocument.uri;
|
||||
const config = previewConfigurations.loadAndCacheConfiguration(sourceUri);
|
||||
const initialData = {
|
||||
previewUri: uri.toString(),
|
||||
source: sourceUri.toString(),
|
||||
line: initialLine,
|
||||
scrollPreviewWithEditorSelection: config.scrollPreviewWithEditorSelection,
|
||||
lineCount: markdownDocument.lineCount,
|
||||
scrollPreviewWithEditor: config.scrollPreviewWithEditor,
|
||||
scrollEditorWithPreview: config.scrollEditorWithPreview,
|
||||
doubleClickToSwitchToEditor: config.doubleClickToSwitchToEditor,
|
||||
disableSecurityWarnings: this.cspArbiter.shouldDisableSecurityWarnings()
|
||||
@@ -256,63 +76,112 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
|
||||
const nonce = new Date().getTime() + '' + new Date().getMilliseconds();
|
||||
const csp = this.getCspForResource(sourceUri, nonce);
|
||||
|
||||
const body = await this.engine.render(sourceUri, config.previewFrontMatter === 'hide', document.getText());
|
||||
const body = await this.engine.render(sourceUri, config.previewFrontMatter === 'hide', markdownDocument.getText());
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
||||
${csp}
|
||||
<meta id="vscode-markdown-preview-data" data-settings="${JSON.stringify(initialData).replace(/"/g, '"')}" data-strings="${JSON.stringify(previewStrings).replace(/"/g, '"')}">
|
||||
<script src="${this.getMediaPath('csp.js')}" nonce="${nonce}"></script>
|
||||
<script src="${this.getMediaPath('loading.js')}" nonce="${nonce}"></script>
|
||||
<script src="${this.extensionResourcePath('csp.js')}" nonce="${nonce}"></script>
|
||||
<script src="${this.extensionResourcePath('loading.js')}" nonce="${nonce}"></script>
|
||||
${this.getStyles(sourceUri, nonce, config)}
|
||||
<base href="${document.uri.toString(true)}">
|
||||
<base href="${markdownDocument.uri.with({ scheme: 'vscode-workspace-resource' }).toString(true)}">
|
||||
</head>
|
||||
<body class="vscode-body ${config.scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${config.wordWrap ? 'wordWrap' : ''} ${config.markEditorSelection ? 'showEditorSelection' : ''}">
|
||||
${body}
|
||||
<div class="code-line" data-line="${document.lineCount}"></div>
|
||||
<div class="code-line" data-line="${markdownDocument.lineCount}"></div>
|
||||
${this.getScripts(nonce)}
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
public updateConfiguration() {
|
||||
// update all generated md documents
|
||||
for (const document of vscode.workspace.textDocuments) {
|
||||
if (document.uri.scheme === 'markdown') {
|
||||
const sourceUri = vscode.Uri.parse(document.uri.query);
|
||||
if (this.previewConfigurations.shouldUpdateConfiguration(sourceUri)) {
|
||||
this.update(document.uri);
|
||||
}
|
||||
private extensionResourcePath(mediaFile: string): string {
|
||||
return vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile)))
|
||||
.with({ scheme: 'vscode-extension-resource' })
|
||||
.toString();
|
||||
}
|
||||
|
||||
private fixHref(resource: vscode.Uri, href: string): string {
|
||||
if (!href) {
|
||||
return href;
|
||||
}
|
||||
|
||||
// Use href if it is already an URL
|
||||
const hrefUri = vscode.Uri.parse(href);
|
||||
if (['http', 'https'].indexOf(hrefUri.scheme) >= 0) {
|
||||
return hrefUri.toString();
|
||||
}
|
||||
|
||||
// Use href as file URI if it is absolute
|
||||
if (path.isAbsolute(href) || hrefUri.scheme === 'file') {
|
||||
return vscode.Uri.file(href)
|
||||
.with({ scheme: 'vscode-workspace-resource' })
|
||||
.toString();
|
||||
}
|
||||
|
||||
// Use a workspace relative path if there is a workspace
|
||||
let root = vscode.workspace.getWorkspaceFolder(resource);
|
||||
if (root) {
|
||||
return vscode.Uri.file(path.join(root.uri.fsPath, href))
|
||||
.with({ scheme: 'vscode-workspace-resource' })
|
||||
.toString();
|
||||
}
|
||||
|
||||
// Otherwise look relative to the markdown file
|
||||
return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))
|
||||
.with({ scheme: 'vscode-workspace-resource' })
|
||||
.toString();
|
||||
}
|
||||
|
||||
private computeCustomStyleSheetIncludes(resource: vscode.Uri, config: MarkdownPreviewConfiguration): string {
|
||||
if (config.styles && Array.isArray(config.styles)) {
|
||||
return config.styles.map(style => {
|
||||
return `<link rel="stylesheet" class="code-user-style" data-source="${style.replace(/"/g, '"')}" href="${this.fixHref(resource, style)}" type="text/css" media="screen">`;
|
||||
}).join('\n');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private getSettingsOverrideStyles(nonce: string, config: MarkdownPreviewConfiguration): string {
|
||||
return `<style nonce="${nonce}">
|
||||
body {
|
||||
${config.fontFamily ? `font-family: ${config.fontFamily};` : ''}
|
||||
${isNaN(config.fontSize) ? '' : `font-size: ${config.fontSize}px;`}
|
||||
${isNaN(config.lineHeight) ? '' : `line-height: ${config.lineHeight};`}
|
||||
}
|
||||
}
|
||||
</style>`;
|
||||
}
|
||||
|
||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||
return this._onDidChange.event;
|
||||
private getStyles(resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfiguration): string {
|
||||
const baseStyles = [
|
||||
this.extensionResourcePath('markdown.css'),
|
||||
this.extensionResourcePath('tomorrow.css')
|
||||
].concat(this.extraStyles.map(resource => resource.toString()));
|
||||
|
||||
return `${baseStyles.map(href => `<link rel="stylesheet" type="text/css" href="${href}">`).join('\n')}
|
||||
${this.getSettingsOverrideStyles(nonce, config)}
|
||||
${this.computeCustomStyleSheetIncludes(resource, config)}`;
|
||||
}
|
||||
|
||||
public update(uri: vscode.Uri) {
|
||||
if (!this._waiting) {
|
||||
this._waiting = true;
|
||||
setTimeout(() => {
|
||||
this._waiting = false;
|
||||
this._onDidChange.fire(uri);
|
||||
}, 300);
|
||||
}
|
||||
private getScripts(nonce: string): string {
|
||||
const scripts = [this.extensionResourcePath('main.js')].concat(this.extraScripts.map(resource => resource.toString()));
|
||||
return scripts
|
||||
.map(source => `<script async src="${source}" nonce="${nonce}" charset="UTF-8"></script>`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
private getCspForResource(resource: vscode.Uri, nonce: string): string {
|
||||
switch (this.cspArbiter.getSecurityLevelForResource(resource)) {
|
||||
case MarkdownPreviewSecurityLevel.AllowInsecureContent:
|
||||
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' http: https: data:; media-src 'self' http: https: data:; script-src 'nonce-${nonce}'; style-src 'self' 'unsafe-inline' http: https: data:; font-src 'self' http: https: data:;">`;
|
||||
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-workspace-resource: vscode-extension-resource: http: https: data:; media-src vscode-workspace-resource: vscode-extension-resource: http: https: data:; script-src 'nonce-${nonce}'; style-src vscode-workspace-resource: 'unsafe-inline' http: https: data: vscode-extension-resource:; font-src vscode-workspace-resource: vscode-extension-resource: http: https: data:;">`;
|
||||
|
||||
case MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent:
|
||||
return '';
|
||||
|
||||
case MarkdownPreviewSecurityLevel.Strict:
|
||||
default:
|
||||
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data:; media-src 'self' https: data:; script-src 'nonce-${nonce}'; style-src 'self' 'unsafe-inline' https: data:; font-src 'self' https: data:;">`;
|
||||
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-workspace-resource: vscode-extension-resource: https: data:; media-src vscode-workspace-resource: vscode-extension-resource: https: data:; script-src 'nonce-${nonce}'; style-src vscode-workspace-resource: 'unsafe-inline' https: data: vscode-extension-resource:; font-src vscode-workspace-resource: vscode-extension-resource: https: data:;">`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
141
extensions/markdown/src/features/previewManager.ts
Normal file
141
extensions/markdown/src/features/previewManager.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { Logger } from '../logger';
|
||||
import { MarkdownContentProvider } from './previewContentProvider';
|
||||
import { MarkdownPreview, PreviewSettings } from './preview';
|
||||
import { disposeAll } from '../util/dispose';
|
||||
import { MarkdownFileTopmostLineMonitor } from '../util/topmostLineMonitor';
|
||||
import { isMarkdownFile } from '../util/file';
|
||||
import { MarkdownPreviewConfigurationManager } from './previewConfig';
|
||||
|
||||
export class MarkdownPreviewManager {
|
||||
private static readonly markdownPreviewActiveContextKey = 'markdownPreviewFocus';
|
||||
|
||||
private readonly topmostLineMonitor = new MarkdownFileTopmostLineMonitor();
|
||||
private readonly previewConfigurations = new MarkdownPreviewConfigurationManager();
|
||||
private readonly previews: MarkdownPreview[] = [];
|
||||
private activePreview: MarkdownPreview | undefined = undefined;
|
||||
private readonly disposables: vscode.Disposable[] = [];
|
||||
|
||||
public constructor(
|
||||
private readonly contentProvider: MarkdownContentProvider,
|
||||
private readonly logger: Logger
|
||||
) {
|
||||
vscode.window.onDidChangeActiveEditor(editor => {
|
||||
vscode.commands.executeCommand('setContext', MarkdownPreviewManager.markdownPreviewActiveContextKey,
|
||||
editor && editor.editorType === 'webview' && editor.uri.scheme === MarkdownPreview.previewScheme);
|
||||
|
||||
this.activePreview = editor && editor.editorType === 'webview'
|
||||
? this.previews.find(preview => editor.uri.toString() === preview.uri.toString())
|
||||
: undefined;
|
||||
|
||||
if (editor && editor.editorType === 'texteditor') {
|
||||
if (isMarkdownFile(editor.document)) {
|
||||
for (const preview of this.previews.filter(preview => !preview.locked)) {
|
||||
preview.update(editor.document.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, null, this.disposables);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
disposeAll(this.disposables);
|
||||
disposeAll(this.previews);
|
||||
}
|
||||
|
||||
public refresh() {
|
||||
for (const preview of this.previews) {
|
||||
preview.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public updateConfiguration() {
|
||||
for (const preview of this.previews) {
|
||||
preview.updateConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
public preview(
|
||||
resource: vscode.Uri,
|
||||
previewSettings: PreviewSettings
|
||||
): void {
|
||||
let preview = this.getExistingPreview(resource, previewSettings);
|
||||
if (preview) {
|
||||
preview.show(previewSettings.previewColumn);
|
||||
} else {
|
||||
preview = this.createNewPreview(resource, previewSettings);
|
||||
this.previews.push(preview);
|
||||
}
|
||||
|
||||
preview.update(resource);
|
||||
}
|
||||
|
||||
public get activePreviewResource() {
|
||||
return this.activePreview && this.activePreview.resource;
|
||||
}
|
||||
|
||||
public getResourceForPreview(previewUri: vscode.Uri): vscode.Uri | undefined {
|
||||
const preview = this.getPreviewWithUri(previewUri);
|
||||
return preview && preview.resource;
|
||||
}
|
||||
|
||||
public toggleLock(previewUri?: vscode.Uri) {
|
||||
const preview = previewUri ? this.getPreviewWithUri(previewUri) : this.activePreview;
|
||||
if (preview) {
|
||||
preview.toggleLock();
|
||||
|
||||
// Close any previews that are now redundant, such as having two dynamic previews in the same editor group
|
||||
for (const otherPreview of this.previews) {
|
||||
if (otherPreview !== preview && preview.matches(otherPreview)) {
|
||||
otherPreview.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getExistingPreview(
|
||||
resource: vscode.Uri,
|
||||
previewSettings: PreviewSettings
|
||||
): MarkdownPreview | undefined {
|
||||
return this.previews.find(preview =>
|
||||
preview.matchesResource(resource, previewSettings.previewColumn, previewSettings.locked));
|
||||
}
|
||||
|
||||
private getPreviewWithUri(previewUri: vscode.Uri): MarkdownPreview | undefined {
|
||||
return this.previews.find(preview => preview.uri.toString() === previewUri.toString());
|
||||
}
|
||||
|
||||
private createNewPreview(
|
||||
resource: vscode.Uri,
|
||||
previewSettings: PreviewSettings
|
||||
) {
|
||||
const preview = new MarkdownPreview(
|
||||
resource,
|
||||
previewSettings.previewColumn,
|
||||
previewSettings.locked,
|
||||
this.contentProvider,
|
||||
this.previewConfigurations,
|
||||
this.logger,
|
||||
this.topmostLineMonitor);
|
||||
|
||||
preview.onDispose(() => {
|
||||
const existing = this.previews.indexOf(preview!);
|
||||
if (existing >= 0) {
|
||||
this.previews.splice(existing, 1);
|
||||
}
|
||||
});
|
||||
|
||||
preview.onDidChangeViewColumn(() => {
|
||||
disposeAll(this.previews.filter(otherPreview => preview !== otherPreview && preview!.matches(otherPreview)));
|
||||
});
|
||||
|
||||
return preview;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { OutputChannel, window, workspace } from 'vscode';
|
||||
|
||||
enum Trace {
|
||||
@@ -32,8 +30,8 @@ function isString(value: any): value is string {
|
||||
}
|
||||
|
||||
export class Logger {
|
||||
private trace: Trace;
|
||||
private _output: OutputChannel;
|
||||
private trace?: Trace;
|
||||
private _output?: OutputChannel;
|
||||
|
||||
constructor() {
|
||||
this.updateConfiguration();
|
||||
@@ -43,7 +41,7 @@ export class Logger {
|
||||
if (this.trace === Trace.Verbose) {
|
||||
this.output.appendLine(`[Log - ${(new Date().toLocaleTimeString())}] ${message}`);
|
||||
if (data) {
|
||||
this.output.appendLine(this.data2String(data));
|
||||
this.output.appendLine(Logger.data2String(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,7 +61,7 @@ export class Logger {
|
||||
return Trace.fromString(workspace.getConfiguration().get<string>('markdown.trace', 'off'));
|
||||
}
|
||||
|
||||
private data2String(data: any): string {
|
||||
private static data2String(data: any): string {
|
||||
if (data instanceof Error) {
|
||||
if (isString(data.stack)) {
|
||||
return data.stack;
|
||||
|
||||
@@ -5,17 +5,17 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import { TableOfContentsProvider } from './tableOfContentsProvider';
|
||||
import { Slug } from './tableOfContentsProvider';
|
||||
import { MarkdownIt, Token } from 'markdown-it';
|
||||
|
||||
const FrontMatterRegex = /^---\s*[^]*?(-{3}|\.{3})\s*/;
|
||||
|
||||
export class MarkdownEngine {
|
||||
private md: MarkdownIt;
|
||||
private md?: MarkdownIt;
|
||||
|
||||
private firstLine: number;
|
||||
private firstLine?: number;
|
||||
|
||||
private currentDocument: vscode.Uri;
|
||||
private currentDocument?: vscode.Uri;
|
||||
|
||||
private plugins: Array<(md: any) => any> = [];
|
||||
|
||||
@@ -51,10 +51,10 @@ export class MarkdownEngine {
|
||||
return `<pre class="hljs"><code><div>${hljs.highlight(lang, str, true).value}</div></code></pre>`;
|
||||
} catch (error) { }
|
||||
}
|
||||
return `<pre class="hljs"><code><div>${this.md.utils.escapeHtml(str)}</div></code></pre>`;
|
||||
return `<pre class="hljs"><code><div>${this.md!.utils.escapeHtml(str)}</div></code></pre>`;
|
||||
}
|
||||
}).use(mdnh, {
|
||||
slugify: (header: string) => TableOfContentsProvider.slugify(header)
|
||||
slugify: (header: string) => Slug.fromHeading(header).value
|
||||
});
|
||||
|
||||
for (const plugin of this.plugins) {
|
||||
@@ -137,17 +137,22 @@ export class MarkdownEngine {
|
||||
md.normalizeLink = (link: string) => {
|
||||
try {
|
||||
let uri = vscode.Uri.parse(link);
|
||||
if (!uri.scheme && uri.path && !uri.fragment) {
|
||||
if (!uri.scheme && uri.path) {
|
||||
// Assume it must be a file
|
||||
const fragment = uri.fragment;
|
||||
if (uri.path[0] === '/') {
|
||||
const root = vscode.workspace.getWorkspaceFolder(this.currentDocument);
|
||||
const root = vscode.workspace.getWorkspaceFolder(this.currentDocument!);
|
||||
if (root) {
|
||||
uri = vscode.Uri.file(path.join(root.uri.fsPath, uri.path));
|
||||
}
|
||||
} else {
|
||||
uri = vscode.Uri.file(path.join(path.dirname(this.currentDocument.path), uri.path));
|
||||
uri = vscode.Uri.file(path.join(path.dirname(this.currentDocument!.path), uri.path));
|
||||
}
|
||||
return normalizeLink(uri.toString(true));
|
||||
|
||||
if (fragment) {
|
||||
uri = uri.with({ fragment });
|
||||
}
|
||||
return normalizeLink(uri.with({ scheme: 'vscode-workspace-resource' }).toString(true));
|
||||
}
|
||||
} catch (e) {
|
||||
// noop
|
||||
|
||||
@@ -6,20 +6,17 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
|
||||
import { MDDocumentContentProvider } from './features/previewContentProvider';
|
||||
import { MarkdownContentProvider } from './features/previewContentProvider';
|
||||
import { MarkdownEngine } from './markdownEngine';
|
||||
|
||||
const resolveExtensionResources = (extension: vscode.Extension<any>, stylePath: string): vscode.Uri => {
|
||||
const resource = vscode.Uri.parse(stylePath);
|
||||
if (resource.scheme) {
|
||||
return resource;
|
||||
}
|
||||
return vscode.Uri.file(path.join(extension.extensionPath, stylePath));
|
||||
const resolveExtensionResources = (extension: vscode.Extension<any>, resourcePath: string): vscode.Uri => {
|
||||
return vscode.Uri.file(path.join(extension.extensionPath, resourcePath))
|
||||
.with({ scheme: 'vscode-extension-resource' });
|
||||
};
|
||||
|
||||
|
||||
export function loadMarkdownExtensions(
|
||||
contentProvider: MDDocumentContentProvider,
|
||||
contentProvider: MarkdownContentProvider,
|
||||
engine: MarkdownEngine
|
||||
) {
|
||||
for (const extension of vscode.extensions.all) {
|
||||
@@ -50,7 +47,7 @@ function tryLoadMarkdownItPlugins(
|
||||
|
||||
function tryLoadPreviewScripts(
|
||||
contributes: any,
|
||||
contentProvider: MDDocumentContentProvider,
|
||||
contentProvider: MarkdownContentProvider,
|
||||
extension: vscode.Extension<any>
|
||||
) {
|
||||
const scripts = contributes['markdown.previewScripts'];
|
||||
@@ -58,8 +55,7 @@ function tryLoadPreviewScripts(
|
||||
for (const script of scripts) {
|
||||
try {
|
||||
contentProvider.addScript(resolveExtensionResources(extension, script));
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
@@ -68,7 +64,7 @@ function tryLoadPreviewScripts(
|
||||
|
||||
function tryLoadPreviewStyles(
|
||||
contributes: any,
|
||||
contentProvider: MDDocumentContentProvider,
|
||||
contentProvider: MarkdownContentProvider,
|
||||
extension: vscode.Extension<any>
|
||||
) {
|
||||
const styles = contributes['markdown.previewStyles'];
|
||||
@@ -76,8 +72,7 @@ function tryLoadPreviewStyles(
|
||||
for (const style of styles) {
|
||||
try {
|
||||
contentProvider.addStyle(resolveExtensionResources(extension, style));
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,317 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import { MarkdownEngine } from './markdownEngine';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import { Logger } from './logger';
|
||||
import { ContentSecurityPolicyArbiter, MarkdownPreviewSecurityLevel } from './security';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
const previewStrings = {
|
||||
cspAlertMessageText: localize('preview.securityMessage.text', 'Some content has been disabled in this document'),
|
||||
cspAlertMessageTitle: localize('preview.securityMessage.title', 'Potentially unsafe or insecure content has been disabled in the markdown preview. Change the Markdown preview security setting to allow insecure content or enable scripts'),
|
||||
cspAlertMessageLabel: localize('preview.securityMessage.label', 'Content Disabled Security Warning')
|
||||
};
|
||||
|
||||
export function isMarkdownFile(document: vscode.TextDocument) {
|
||||
return document.languageId === 'markdown'
|
||||
&& document.uri.scheme !== 'markdown'; // prevent processing of own documents
|
||||
}
|
||||
|
||||
export function getMarkdownUri(uri: vscode.Uri) {
|
||||
if (uri.scheme === 'markdown') {
|
||||
return uri;
|
||||
}
|
||||
|
||||
return uri.with({
|
||||
scheme: 'markdown',
|
||||
path: uri.path + '.rendered',
|
||||
query: uri.toString()
|
||||
});
|
||||
}
|
||||
|
||||
class MarkdownPreviewConfig {
|
||||
public static getConfigForResource(resource: vscode.Uri) {
|
||||
return new MarkdownPreviewConfig(resource);
|
||||
}
|
||||
|
||||
public readonly scrollBeyondLastLine: boolean;
|
||||
public readonly wordWrap: boolean;
|
||||
public readonly previewFrontMatter: string;
|
||||
public readonly lineBreaks: boolean;
|
||||
public readonly doubleClickToSwitchToEditor: boolean;
|
||||
public readonly scrollEditorWithPreview: boolean;
|
||||
public readonly scrollPreviewWithEditorSelection: boolean;
|
||||
public readonly markEditorSelection: boolean;
|
||||
|
||||
public readonly lineHeight: number;
|
||||
public readonly fontSize: number;
|
||||
public readonly fontFamily: string | undefined;
|
||||
public readonly styles: string[];
|
||||
|
||||
private constructor(resource: vscode.Uri) {
|
||||
const editorConfig = vscode.workspace.getConfiguration('editor', resource);
|
||||
const markdownConfig = vscode.workspace.getConfiguration('markdown', resource);
|
||||
const markdownEditorConfig = vscode.workspace.getConfiguration('[markdown]');
|
||||
|
||||
this.scrollBeyondLastLine = editorConfig.get<boolean>('scrollBeyondLastLine', false);
|
||||
|
||||
this.wordWrap = editorConfig.get<string>('wordWrap', 'off') !== 'off';
|
||||
if (markdownEditorConfig && markdownEditorConfig['editor.wordWrap']) {
|
||||
this.wordWrap = markdownEditorConfig['editor.wordWrap'] !== 'off';
|
||||
}
|
||||
|
||||
this.previewFrontMatter = markdownConfig.get<string>('previewFrontMatter', 'hide');
|
||||
this.scrollPreviewWithEditorSelection = !!markdownConfig.get<boolean>('preview.scrollPreviewWithEditorSelection', true);
|
||||
this.scrollEditorWithPreview = !!markdownConfig.get<boolean>('preview.scrollEditorWithPreview', true);
|
||||
this.lineBreaks = !!markdownConfig.get<boolean>('preview.breaks', false);
|
||||
this.doubleClickToSwitchToEditor = !!markdownConfig.get<boolean>('preview.doubleClickToSwitchToEditor', true);
|
||||
this.markEditorSelection = !!markdownConfig.get<boolean>('preview.markEditorSelection', true);
|
||||
|
||||
this.fontFamily = markdownConfig.get<string | undefined>('preview.fontFamily', undefined);
|
||||
this.fontSize = Math.max(8, +markdownConfig.get<number>('preview.fontSize', NaN));
|
||||
this.lineHeight = Math.max(0.6, +markdownConfig.get<number>('preview.lineHeight', NaN));
|
||||
|
||||
this.styles = markdownConfig.get<string[]>('styles', []);
|
||||
}
|
||||
|
||||
public isEqualTo(otherConfig: MarkdownPreviewConfig) {
|
||||
for (let key in this) {
|
||||
if (this.hasOwnProperty(key) && key !== 'styles') {
|
||||
if (this[key] !== otherConfig[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check styles
|
||||
if (this.styles.length !== otherConfig.styles.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < this.styles.length; ++i) {
|
||||
if (this.styles[i] !== otherConfig.styles[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
class PreviewConfigManager {
|
||||
private previewConfigurationsForWorkspaces = new Map<string, MarkdownPreviewConfig>();
|
||||
|
||||
public loadAndCacheConfiguration(
|
||||
resource: vscode.Uri
|
||||
) {
|
||||
const config = MarkdownPreviewConfig.getConfigForResource(resource);
|
||||
this.previewConfigurationsForWorkspaces.set(this.getKey(resource), config);
|
||||
return config;
|
||||
}
|
||||
|
||||
public shouldUpdateConfiguration(
|
||||
resource: vscode.Uri
|
||||
): boolean {
|
||||
const key = this.getKey(resource);
|
||||
const currentConfig = this.previewConfigurationsForWorkspaces.get(key);
|
||||
const newConfig = MarkdownPreviewConfig.getConfigForResource(resource);
|
||||
return (!currentConfig || !currentConfig.isEqualTo(newConfig));
|
||||
}
|
||||
|
||||
private getKey(
|
||||
resource: vscode.Uri
|
||||
): string {
|
||||
const folder = vscode.workspace.getWorkspaceFolder(resource);
|
||||
if (!folder) {
|
||||
return '';
|
||||
}
|
||||
return folder.uri.toString();
|
||||
}
|
||||
}
|
||||
|
||||
export class MDDocumentContentProvider implements vscode.TextDocumentContentProvider {
|
||||
private _onDidChange = new vscode.EventEmitter<vscode.Uri>();
|
||||
private _waiting: boolean = false;
|
||||
private previewConfigurations = new PreviewConfigManager();
|
||||
|
||||
private extraStyles: Array<vscode.Uri> = [];
|
||||
private extraScripts: Array<vscode.Uri> = [];
|
||||
|
||||
constructor(
|
||||
private engine: MarkdownEngine,
|
||||
private context: vscode.ExtensionContext,
|
||||
private cspArbiter: ContentSecurityPolicyArbiter,
|
||||
private logger: Logger
|
||||
) { }
|
||||
|
||||
public addScript(resource: vscode.Uri): void {
|
||||
this.extraScripts.push(resource);
|
||||
}
|
||||
|
||||
public addStyle(resource: vscode.Uri): void {
|
||||
this.extraStyles.push(resource);
|
||||
}
|
||||
|
||||
private getMediaPath(mediaFile: string): string {
|
||||
return vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))).toString();
|
||||
}
|
||||
|
||||
private fixHref(resource: vscode.Uri, href: string): string {
|
||||
if (!href) {
|
||||
return href;
|
||||
}
|
||||
|
||||
// Use href if it is already an URL
|
||||
const hrefUri = vscode.Uri.parse(href);
|
||||
if (['file', 'http', 'https'].indexOf(hrefUri.scheme) >= 0) {
|
||||
return hrefUri.toString();
|
||||
}
|
||||
|
||||
// Use href as file URI if it is absolute
|
||||
if (path.isAbsolute(href)) {
|
||||
return vscode.Uri.file(href).toString();
|
||||
}
|
||||
|
||||
// use a workspace relative path if there is a workspace
|
||||
let root = vscode.workspace.getWorkspaceFolder(resource);
|
||||
if (root) {
|
||||
return vscode.Uri.file(path.join(root.uri.fsPath, href)).toString();
|
||||
}
|
||||
|
||||
// otherwise look relative to the markdown file
|
||||
return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)).toString();
|
||||
}
|
||||
|
||||
private computeCustomStyleSheetIncludes(resource: vscode.Uri, config: MarkdownPreviewConfig): string {
|
||||
if (config.styles && Array.isArray(config.styles)) {
|
||||
return config.styles.map(style => {
|
||||
return `<link rel="stylesheet" class="code-user-style" data-source="${style.replace(/"/g, '"')}" href="${this.fixHref(resource, style)}" type="text/css" media="screen">`;
|
||||
}).join('\n');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private getSettingsOverrideStyles(nonce: string, config: MarkdownPreviewConfig): string {
|
||||
return `<style nonce="${nonce}">
|
||||
body {
|
||||
${config.fontFamily ? `font-family: ${config.fontFamily};` : ''}
|
||||
${isNaN(config.fontSize) ? '' : `font-size: ${config.fontSize}px;`}
|
||||
${isNaN(config.lineHeight) ? '' : `line-height: ${config.lineHeight};`}
|
||||
}
|
||||
</style>`;
|
||||
}
|
||||
|
||||
private getStyles(resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfig): string {
|
||||
const baseStyles = [
|
||||
this.getMediaPath('markdown.css'),
|
||||
this.getMediaPath('tomorrow.css')
|
||||
].concat(this.extraStyles.map(resource => resource.toString()));
|
||||
|
||||
return `${baseStyles.map(href => `<link rel="stylesheet" type="text/css" href="${href}">`).join('\n')}
|
||||
${this.getSettingsOverrideStyles(nonce, config)}
|
||||
${this.computeCustomStyleSheetIncludes(resource, config)}`;
|
||||
}
|
||||
|
||||
private getScripts(nonce: string): string {
|
||||
const scripts = [this.getMediaPath('main.js')].concat(this.extraScripts.map(resource => resource.toString()));
|
||||
return scripts
|
||||
.map(source => `<script async src="${source}" nonce="${nonce}" charset="UTF-8"></script>`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
public async provideTextDocumentContent(uri: vscode.Uri): Promise<string> {
|
||||
const sourceUri = vscode.Uri.parse(uri.query);
|
||||
|
||||
let initialLine: number | undefined = undefined;
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (editor && editor.document.uri.toString() === sourceUri.toString()) {
|
||||
initialLine = editor.selection.active.line;
|
||||
}
|
||||
|
||||
const document = await vscode.workspace.openTextDocument(sourceUri);
|
||||
const config = this.previewConfigurations.loadAndCacheConfiguration(sourceUri);
|
||||
|
||||
const initialData = {
|
||||
previewUri: uri.toString(),
|
||||
source: sourceUri.toString(),
|
||||
line: initialLine,
|
||||
scrollPreviewWithEditorSelection: config.scrollPreviewWithEditorSelection,
|
||||
scrollEditorWithPreview: config.scrollEditorWithPreview,
|
||||
doubleClickToSwitchToEditor: config.doubleClickToSwitchToEditor
|
||||
};
|
||||
|
||||
this.logger.log('provideTextDocumentContent', initialData);
|
||||
|
||||
// Content Security Policy
|
||||
const nonce = new Date().getTime() + '' + new Date().getMilliseconds();
|
||||
const csp = this.getCspForResource(sourceUri, nonce);
|
||||
|
||||
const body = await this.engine.render(sourceUri, config.previewFrontMatter === 'hide', document.getText());
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
||||
${csp}
|
||||
<meta id="vscode-markdown-preview-data" data-settings="${JSON.stringify(initialData).replace(/"/g, '"')}" data-strings="${JSON.stringify(previewStrings).replace(/"/g, '"')}">
|
||||
<script src="${this.getMediaPath('csp.js')}" nonce="${nonce}"></script>
|
||||
<script src="${this.getMediaPath('loading.js')}" nonce="${nonce}"></script>
|
||||
${this.getStyles(sourceUri, nonce, config)}
|
||||
<base href="${document.uri.toString(true)}">
|
||||
</head>
|
||||
<body class="vscode-body ${config.scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${config.wordWrap ? 'wordWrap' : ''} ${config.markEditorSelection ? 'showEditorSelection' : ''}">
|
||||
${body}
|
||||
<div class="code-line" data-line="${document.lineCount}"></div>
|
||||
${this.getScripts(nonce)}
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
public updateConfiguration() {
|
||||
// update all generated md documents
|
||||
for (const document of vscode.workspace.textDocuments) {
|
||||
if (document.uri.scheme === 'markdown') {
|
||||
const sourceUri = vscode.Uri.parse(document.uri.query);
|
||||
if (this.previewConfigurations.shouldUpdateConfiguration(sourceUri)) {
|
||||
this.update(document.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||
return this._onDidChange.event;
|
||||
}
|
||||
|
||||
public update(uri: vscode.Uri) {
|
||||
if (!this._waiting) {
|
||||
this._waiting = true;
|
||||
setTimeout(() => {
|
||||
this._waiting = false;
|
||||
this._onDidChange.fire(uri);
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
private getCspForResource(resource: vscode.Uri, nonce: string): string {
|
||||
switch (this.cspArbiter.getSecurityLevelForResource(resource)) {
|
||||
case MarkdownPreviewSecurityLevel.AllowInsecureContent:
|
||||
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' http: https: data:; media-src 'self' http: https: data:; script-src 'nonce-${nonce}'; style-src 'self' 'unsafe-inline' http: https: data:; font-src 'self' http: https: data:;">`;
|
||||
|
||||
case MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent:
|
||||
return '';
|
||||
|
||||
case MarkdownPreviewSecurityLevel.Strict:
|
||||
default:
|
||||
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data:; media-src 'self' https: data:; script-src 'nonce-${nonce}'; style-src 'self' 'unsafe-inline' https: data:; font-src 'self' https: data:;">`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,10 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { getMarkdownUri, MDDocumentContentProvider } from './features/previewContentProvider';
|
||||
import { MarkdownPreviewManager } from './features/previewManager';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
@@ -36,8 +35,8 @@ export class ExtensionContentSecurityPolicyArbiter implements ContentSecurityPol
|
||||
private readonly should_disable_security_warning_key = 'preview_should_show_security_warning:';
|
||||
|
||||
constructor(
|
||||
private globalState: vscode.Memento,
|
||||
private workspaceState: vscode.Memento
|
||||
private readonly globalState: vscode.Memento,
|
||||
private readonly workspaceState: vscode.Memento
|
||||
) { }
|
||||
|
||||
public getSecurityLevelForResource(resource: vscode.Uri): MarkdownPreviewSecurityLevel {
|
||||
@@ -90,13 +89,13 @@ export class ExtensionContentSecurityPolicyArbiter implements ContentSecurityPol
|
||||
export class PreviewSecuritySelector {
|
||||
|
||||
public constructor(
|
||||
private cspArbiter: ContentSecurityPolicyArbiter,
|
||||
private contentProvider: MDDocumentContentProvider
|
||||
private readonly cspArbiter: ContentSecurityPolicyArbiter,
|
||||
private readonly webviewManager: MarkdownPreviewManager
|
||||
) { }
|
||||
|
||||
public async showSecutitySelectorForResource(resource: vscode.Uri): Promise<void> {
|
||||
interface PreviewSecurityPickItem extends vscode.QuickPickItem {
|
||||
type: 'moreinfo' | 'toggle' | MarkdownPreviewSecurityLevel;
|
||||
readonly type: 'moreinfo' | 'toggle' | MarkdownPreviewSecurityLevel;
|
||||
}
|
||||
|
||||
function markActiveWhen(when: boolean): string {
|
||||
@@ -127,7 +126,7 @@ export class PreviewSecuritySelector {
|
||||
label: this.cspArbiter.shouldDisableSecurityWarnings()
|
||||
? localize('enableSecurityWarning.title', "Enable preview security warnings in this workspace")
|
||||
: localize('disableSecurityWarning.title', "Disable preview security warning in this workspace"),
|
||||
description: localize('toggleSecurityWarning.description', 'Does not effect the content security level')
|
||||
description: localize('toggleSecurityWarning.description', 'Does not affect the content security level')
|
||||
},
|
||||
], {
|
||||
placeHolder: localize(
|
||||
@@ -144,22 +143,12 @@ export class PreviewSecuritySelector {
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceUri = getMarkdownUri(resource);
|
||||
if (selection.type === 'toggle') {
|
||||
this.cspArbiter.setShouldDisableSecurityWarning(!this.cspArbiter.shouldDisableSecurityWarnings());
|
||||
this.contentProvider.update(sourceUri);
|
||||
return;
|
||||
} else {
|
||||
await this.cspArbiter.setSecurityLevelForResource(resource, selection.type);
|
||||
}
|
||||
|
||||
await this.cspArbiter.setSecurityLevelForResource(resource, selection.type);
|
||||
|
||||
await vscode.commands.executeCommand('_workbench.htmlPreview.updateOptions',
|
||||
sourceUri,
|
||||
{
|
||||
allowScripts: true,
|
||||
allowSvgs: this.cspArbiter.shouldAllowSvgsForResource(resource)
|
||||
});
|
||||
|
||||
this.contentProvider.update(sourceUri);
|
||||
this.webviewManager.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,22 +3,41 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { MarkdownEngine } from './markdownEngine';
|
||||
|
||||
export class Slug {
|
||||
public static fromHeading(heading: string): Slug {
|
||||
const slugifiedHeading = encodeURI(heading.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/^\-+/, '')
|
||||
.replace(/\-+$/, ''));
|
||||
|
||||
return new Slug(slugifiedHeading);
|
||||
}
|
||||
|
||||
private constructor(
|
||||
public readonly value: string
|
||||
) { }
|
||||
|
||||
public equals(other: Slug): boolean {
|
||||
return this.value === other.value;
|
||||
}
|
||||
}
|
||||
|
||||
export interface TocEntry {
|
||||
slug: string;
|
||||
text: string;
|
||||
level: number;
|
||||
line: number;
|
||||
location: vscode.Location;
|
||||
readonly slug: Slug;
|
||||
readonly text: string;
|
||||
readonly level: number;
|
||||
readonly line: number;
|
||||
readonly location: vscode.Location;
|
||||
}
|
||||
|
||||
export class TableOfContentsProvider {
|
||||
private toc: TocEntry[];
|
||||
private toc?: TocEntry[];
|
||||
|
||||
public constructor(
|
||||
private engine: MarkdownEngine,
|
||||
@@ -36,14 +55,10 @@ export class TableOfContentsProvider {
|
||||
return this.toc;
|
||||
}
|
||||
|
||||
public async lookup(fragment: string): Promise<number> {
|
||||
const slug = TableOfContentsProvider.slugify(fragment);
|
||||
for (const entry of await this.getToc()) {
|
||||
if (entry.slug === slug) {
|
||||
return entry.line;
|
||||
}
|
||||
}
|
||||
return NaN;
|
||||
public async lookup(fragment: string): Promise<TocEntry | undefined> {
|
||||
const toc = await this.getToc();
|
||||
const slug = Slug.fromHeading(fragment);
|
||||
return toc.find(entry => entry.slug.equals(slug));
|
||||
}
|
||||
|
||||
private async buildToc(document: vscode.TextDocument): Promise<TocEntry[]> {
|
||||
@@ -53,17 +68,13 @@ export class TableOfContentsProvider {
|
||||
for (const heading of tokens.filter(token => token.type === 'heading_open')) {
|
||||
const lineNumber = heading.map[0];
|
||||
const line = document.lineAt(lineNumber);
|
||||
const href = TableOfContentsProvider.slugify(line.text);
|
||||
const level = TableOfContentsProvider.getHeaderLevel(heading.markup);
|
||||
if (href) {
|
||||
toc.push({
|
||||
slug: href,
|
||||
text: TableOfContentsProvider.getHeaderText(line.text),
|
||||
level: level,
|
||||
line: lineNumber,
|
||||
location: new vscode.Location(document.uri, line.range)
|
||||
});
|
||||
}
|
||||
toc.push({
|
||||
slug: Slug.fromHeading(line.text),
|
||||
text: TableOfContentsProvider.getHeaderText(line.text),
|
||||
level: TableOfContentsProvider.getHeaderLevel(heading.markup),
|
||||
line: lineNumber,
|
||||
location: new vscode.Location(document.uri, line.range)
|
||||
});
|
||||
}
|
||||
return toc;
|
||||
}
|
||||
@@ -81,14 +92,4 @@ export class TableOfContentsProvider {
|
||||
private static getHeaderText(header: string): string {
|
||||
return header.replace(/^\s*#+\s*(.*?)\s*#*$/, (_, word) => word.trim());
|
||||
}
|
||||
|
||||
public static slugify(header: string): string {
|
||||
return encodeURI(header.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/^\-+/, '')
|
||||
.replace(/\-+$/, ''));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
28
extensions/markdown/src/test/index.ts
Normal file
28
extensions/markdown/src/test/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
//
|
||||
// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
|
||||
//
|
||||
// This file is providing the test runner to use when running extension tests.
|
||||
// By default the test runner in use is Mocha based.
|
||||
//
|
||||
// You can provide your own test runner if you want to override it by exporting
|
||||
// a function run(testRoot: string, clb: (error:Error) => void) that the extension
|
||||
// host can call to run the tests. The test runner is expected to use console.log
|
||||
// to report the results back to the caller. When the tests are finished, return
|
||||
// a possible error to the callback or null if none.
|
||||
|
||||
const testRunner = require('vscode/lib/testrunner');
|
||||
|
||||
// You can directly control Mocha options by uncommenting the following lines
|
||||
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
|
||||
testRunner.configure({
|
||||
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
|
||||
useColors: process.platform !== 'win32', // colored output from test results (only windows cannot handle)
|
||||
timeout: 60000
|
||||
});
|
||||
|
||||
export = testRunner;
|
||||
131
extensions/markdown/src/test/tableOfContentsProvider.test.ts
Normal file
131
extensions/markdown/src/test/tableOfContentsProvider.test.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as vscode from 'vscode';
|
||||
import 'mocha';
|
||||
|
||||
import { TableOfContentsProvider } from '../tableOfContentsProvider';
|
||||
import { MarkdownEngine } from '../markdownEngine';
|
||||
|
||||
const testFileName = vscode.Uri.parse('test.md');
|
||||
|
||||
suite('markdown.TableOfContentsProvider', () => {
|
||||
test('Lookup should not return anything for empty document', async () => {
|
||||
const doc = new InMemoryDocument(testFileName, '');
|
||||
const provider = new TableOfContentsProvider(new MarkdownEngine(), doc);
|
||||
|
||||
assert.strictEqual(await provider.lookup(''), undefined);
|
||||
assert.strictEqual(await provider.lookup('foo'), undefined);
|
||||
});
|
||||
|
||||
test('Lookup should not return anything for document with no headers', async () => {
|
||||
const doc = new InMemoryDocument(testFileName, 'a *b*\nc');
|
||||
const provider = new TableOfContentsProvider(new MarkdownEngine(), doc);
|
||||
|
||||
assert.strictEqual(await provider.lookup(''), undefined);
|
||||
assert.strictEqual(await provider.lookup('foo'), undefined);
|
||||
assert.strictEqual(await provider.lookup('a'), undefined);
|
||||
assert.strictEqual(await provider.lookup('b'), undefined);
|
||||
});
|
||||
|
||||
test('Lookup should return basic #header', async () => {
|
||||
const doc = new InMemoryDocument(testFileName, `# a\nx\n# c`);
|
||||
const provider = new TableOfContentsProvider(new MarkdownEngine(), doc);
|
||||
|
||||
{
|
||||
const entry = await provider.lookup('a');
|
||||
assert.ok(entry);
|
||||
assert.strictEqual(entry!.line, 0);
|
||||
}
|
||||
{
|
||||
assert.strictEqual(await provider.lookup('x'), undefined);
|
||||
}
|
||||
{
|
||||
const entry = await provider.lookup('c');
|
||||
assert.ok(entry);
|
||||
assert.strictEqual(entry!.line, 2);
|
||||
}
|
||||
});
|
||||
|
||||
test('Lookups should be case in-sensitive', async () => {
|
||||
const doc = new InMemoryDocument(testFileName, `# fOo\n`);
|
||||
const provider = new TableOfContentsProvider(new MarkdownEngine(), doc);
|
||||
|
||||
assert.strictEqual((await provider.lookup('fOo'))!.line, 0);
|
||||
assert.strictEqual((await provider.lookup('foo'))!.line, 0);
|
||||
assert.strictEqual((await provider.lookup('FOO'))!.line, 0);
|
||||
});
|
||||
|
||||
test('Lookups should ignore leading and trailing white-space, and collapse internal whitespace', async () => {
|
||||
const doc = new InMemoryDocument(testFileName, `# f o o \n`);
|
||||
const provider = new TableOfContentsProvider(new MarkdownEngine(), doc);
|
||||
|
||||
assert.strictEqual((await provider.lookup('f o o'))!.line, 0);
|
||||
assert.strictEqual((await provider.lookup(' f o o'))!.line, 0);
|
||||
assert.strictEqual((await provider.lookup(' f o o '))!.line, 0);
|
||||
assert.strictEqual((await provider.lookup('f o o'))!.line, 0);
|
||||
assert.strictEqual((await provider.lookup('f o o'))!.line, 0);
|
||||
|
||||
assert.strictEqual(await provider.lookup('f'), undefined);
|
||||
assert.strictEqual(await provider.lookup('foo'), undefined);
|
||||
assert.strictEqual(await provider.lookup('fo o'), undefined);
|
||||
});
|
||||
});
|
||||
|
||||
class InMemoryDocument implements vscode.TextDocument {
|
||||
private readonly _lines: string[];
|
||||
|
||||
constructor(
|
||||
public readonly uri: vscode.Uri,
|
||||
private readonly _contents: string
|
||||
) {
|
||||
this._lines = this._contents.split(/\n/g);
|
||||
}
|
||||
|
||||
fileName: string = '';
|
||||
isUntitled: boolean = false;
|
||||
languageId: string = '';
|
||||
version: number = 1;
|
||||
isDirty: boolean = false;
|
||||
isClosed: boolean = false;
|
||||
eol: vscode.EndOfLine = vscode.EndOfLine.LF;
|
||||
|
||||
get lineCount(): number {
|
||||
return this._lines.length;
|
||||
}
|
||||
|
||||
lineAt(line: any): vscode.TextLine {
|
||||
return {
|
||||
lineNumber: line,
|
||||
text: this._lines[line],
|
||||
range: new vscode.Range(0, 0, 0, 0),
|
||||
firstNonWhitespaceCharacterIndex: 0,
|
||||
rangeIncludingLineBreak: new vscode.Range(0, 0, 0, 0),
|
||||
isEmptyOrWhitespace: false
|
||||
};
|
||||
}
|
||||
offsetAt(_position: vscode.Position): never {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
positionAt(_offset: number): never {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getText(_range?: vscode.Range | undefined): string {
|
||||
return this._contents;
|
||||
}
|
||||
getWordRangeAtPosition(_position: vscode.Position, _regex?: RegExp | undefined): never {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
validateRange(_range: vscode.Range): never {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
validatePosition(_position: vscode.Position): never {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
save(): never {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
1
extensions/markdown/src/typings/ref.d.ts
vendored
1
extensions/markdown/src/typings/ref.d.ts
vendored
@@ -4,4 +4,5 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||
/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/>
|
||||
/// <reference types='@types/node'/>
|
||||
|
||||
16
extensions/markdown/src/util/dispose.ts
Normal file
16
extensions/markdown/src/util/dispose.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export function disposeAll(disposables: vscode.Disposable[]) {
|
||||
while (disposables.length) {
|
||||
const item = disposables.pop();
|
||||
if (item) {
|
||||
item.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
extensions/markdown/src/util/file.ts
Normal file
10
extensions/markdown/src/util/file.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export function isMarkdownFile(document: vscode.TextDocument) {
|
||||
return document.languageId === 'markdown';
|
||||
}
|
||||
75
extensions/markdown/src/util/topmostLineMonitor.ts
Normal file
75
extensions/markdown/src/util/topmostLineMonitor.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { disposeAll } from '../util/dispose';
|
||||
import { isMarkdownFile } from './file';
|
||||
|
||||
|
||||
export class MarkdownFileTopmostLineMonitor {
|
||||
private readonly disposables: vscode.Disposable[] = [];
|
||||
|
||||
private readonly pendingUpdates = new Map<string, number>();
|
||||
|
||||
constructor() {
|
||||
vscode.window.onDidChangeTextEditorVisibleRanges(event => {
|
||||
if (isMarkdownFile(event.textEditor.document)) {
|
||||
const line = getVisibleLine(event.textEditor);
|
||||
if (line) {
|
||||
this.updateLine(event.textEditor.document.uri, line);
|
||||
}
|
||||
}
|
||||
}, null, this.disposables);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
disposeAll(this.disposables);
|
||||
}
|
||||
|
||||
private readonly _onDidChangeTopmostLineEmitter = new vscode.EventEmitter<{ resource: vscode.Uri, line: number }>();
|
||||
public readonly onDidChangeTopmostLine = this._onDidChangeTopmostLineEmitter.event;
|
||||
|
||||
private updateLine(
|
||||
resource: vscode.Uri,
|
||||
line: number
|
||||
) {
|
||||
const key = resource.toString();
|
||||
if (!this.pendingUpdates.has(key)) {
|
||||
// schedule update
|
||||
setTimeout(() => {
|
||||
if (this.pendingUpdates.has(key)) {
|
||||
this._onDidChangeTopmostLineEmitter.fire({
|
||||
resource,
|
||||
line: this.pendingUpdates.get(key) as number
|
||||
});
|
||||
this.pendingUpdates.delete(key);
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
|
||||
this.pendingUpdates.set(key, line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the top-most visible range of `editor`.
|
||||
*
|
||||
* Returns a fractional line number based the visible character within the line.
|
||||
* Floor to get real line number
|
||||
*/
|
||||
export function getVisibleLine(
|
||||
editor: vscode.TextEditor
|
||||
): number | undefined {
|
||||
if (!editor.visibleRanges.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const firstVisiblePosition = editor.visibleRanges[0].start;
|
||||
const lineNumber = firstVisiblePosition.line;
|
||||
const line = editor.document.lineAt(lineNumber);
|
||||
const progress = firstVisiblePosition.character / (line.text.length + 2);
|
||||
return lineNumber + progress;
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
var gulp = require('gulp');
|
||||
var util = require("gulp-util");
|
||||
var replace = require('gulp-replace');
|
||||
var rename = require('gulp-rename');
|
||||
|
||||
const languages = [
|
||||
{ name: 'css', language: 'css', identifiers: ['css', 'css.erb'], source: 'source.css' },
|
||||
{ name: 'basic', language: 'html', identifiers: ['html', 'htm', 'shtml', 'xhtml', 'inc', 'tmpl', 'tpl'], source: 'text.html.basic' },
|
||||
{ name: 'ini', language: 'ini', identifiers: ['ini', 'conf'], source: 'source.ini' },
|
||||
{ name: 'java', language: 'java', identifiers: ['java', 'bsh'], source: 'source.java' },
|
||||
{ name: 'lua', language: 'lua', identifiers: ['lua'], source: 'source.lua' },
|
||||
{ name: 'makefile', language: 'makefile', identifiers: ['Makefile', 'makefile', 'GNUmakefile', 'OCamlMakefile'], source: 'source.makefile' },
|
||||
{ name: 'perl', language: 'perl', identifiers: ['perl', 'pl', 'pm', 'pod', 't', 'PL', 'psgi', 'vcl'], source: 'source.perl' },
|
||||
{ name: 'r', language: 'r', identifiers: ['R', 'r', 's', 'S', 'Rprofile'], source: 'source.r' },
|
||||
{ name: 'ruby', language: 'ruby', identifiers: ['ruby', 'rb', 'rbx', 'rjs', 'Rakefile', 'rake', 'cgi', 'fcgi', 'gemspec', 'irbrc', 'Capfile', 'ru', 'prawn', 'Cheffile', 'Gemfile', 'Guardfile', 'Hobofile', 'Vagrantfile', 'Appraisals', 'Rantfile', 'Berksfile', 'Berksfile.lock', 'Thorfile', 'Puppetfile'], source: 'source.ruby' },
|
||||
// Left to its own devices, the PHP grammar will match HTML as a combination of operators
|
||||
// and constants. Therefore, HTML must take precedence over PHP in order to get proper
|
||||
// syntax highlighting.
|
||||
{ name: 'php', language: 'php', identifiers: ['php', 'php3', 'php4', 'php5', 'phpt', 'phtml', 'aw', 'ctp'], source: ['text.html.basic', 'text.html.php#language'] },
|
||||
{ name: 'sql', language: 'sql', identifiers: ['sql', 'ddl', 'dml'], source: 'source.sql' },
|
||||
{ name: 'vs_net', language: 'vs_net', identifiers: ['vb'], source: 'source.asp.vb.net' },
|
||||
{ name: 'xml', language: 'xml', identifiers: ['xml', 'xsd', 'tld', 'jsp', 'pt', 'cpt', 'dtml', 'rss', 'opml'], source: 'text.xml' },
|
||||
{ name: 'xsl', language: 'xsl', identifiers: ['xsl', 'xslt'], source: 'text.xml.xsl' },
|
||||
{ name: 'yaml', language: 'yaml', identifiers: ['yaml', 'yml'], source: 'source.yaml' },
|
||||
{ name: 'dosbatch', language: 'dosbatch', identifiers: ['bat', 'batch'], source: 'source.dosbatch' },
|
||||
{ name: 'clojure', language: 'clojure', identifiers: ['clj', 'cljs', 'clojure'], source: 'source.clojure' },
|
||||
{ name: 'coffee', language: 'coffee', identifiers: ['coffee', 'Cakefile', 'coffee.erb'], source: 'source.coffee' },
|
||||
{ name: 'c', language: 'c', identifiers: ['c', 'h'], source: 'source.c' },
|
||||
{ name: 'cpp', language: 'cpp', identifiers: ['cpp', 'c\\+\\+', 'cxx'], source: 'source.cpp' },
|
||||
{ name: 'diff', language: 'diff', identifiers: ['patch', 'diff', 'rej'], source: 'source.diff' },
|
||||
{ name: 'dockerfile', language: 'dockerfile', identifiers: ['dockerfile', 'Dockerfile'], source: 'source.dockerfile' },
|
||||
{ name: 'git_commit', identifiers: ['COMMIT_EDITMSG', 'MERGE_MSG'], source: 'text.git-commit' },
|
||||
{ name: 'git_rebase', identifiers: ['git-rebase-todo'], source: 'text.git-rebase' },
|
||||
{ name: 'go', language: 'go', identifiers: ['go', 'golang'], source: 'source.go' },
|
||||
{ name: 'groovy', language: 'groovy', identifiers: ['groovy', 'gvy'], source: 'source.groovy' },
|
||||
{ name: 'jade', language: 'jade', identifiers: ['jade', 'pug'], source: 'text.jade' },
|
||||
|
||||
{ name: 'js', language: 'javascript', identifiers: ['js', 'jsx', 'javascript', 'es6', 'mjs'], source: 'source.js' },
|
||||
{ name: 'js_regexp', identifiers: ['regexp'], source: 'source.js.regexp' },
|
||||
{ name: 'json', language: 'json', identifiers: ['json', 'sublime-settings', 'sublime-menu', 'sublime-keymap', 'sublime-mousemap', 'sublime-theme', 'sublime-build', 'sublime-project', 'sublime-completions'], source: 'source.json' },
|
||||
{ name: 'less', language: 'less', identifiers: ['less'], source: 'source.css.less' },
|
||||
{ name: 'objc', language: 'objc', identifiers: ['objectivec', 'objective-c', 'mm', 'objc', 'obj-c', 'm', 'h'], source: 'source.objc' },
|
||||
{ name: 'scss', language: 'scss', identifiers: ['scss'], source: 'source.css.scss' },
|
||||
|
||||
{ name: 'perl6', language: 'perl6', identifiers: ['perl6', 'p6', 'pl6', 'pm6', 'nqp'], source: 'source.perl.6' },
|
||||
{ name: 'powershell', language: 'powershell', identifiers: ['powershell', 'ps1', 'psm1', 'psd1'], source: 'source.powershell' },
|
||||
{ name: 'python', language: 'python', identifiers: ['python', 'py', 'py3', 'rpy', 'pyw', 'cpy', 'SConstruct', 'Sconstruct', 'sconstruct', 'SConscript', 'gyp', 'gypi'], source: 'source.python' },
|
||||
{ name: 'regexp_python', identifiers: ['re'], source: 'source.regexp.python' },
|
||||
{ name: 'rust', language: 'rust', identifiers: ['rust', 'rs'], source: 'source.rust' },
|
||||
{ name: 'scala', language: 'scala', identifiers: ['scala', 'sbt'], source: 'source.scala' },
|
||||
{ name: 'shell', language: 'shellscript', identifiers: ['shell', 'sh', 'bash', 'zsh', 'bashrc', 'bash_profile', 'bash_login', 'profile', 'bash_logout', '.textmate_init'], source: 'source.shell' },
|
||||
{ name: 'ts', language: 'typescript', identifiers: ['typescript', 'ts'], source: 'source.ts' },
|
||||
{ name: 'tsx', language: 'typescriptreact', identifiers: ['tsx'], source: 'source.tsx' },
|
||||
{ name: 'csharp', language: 'csharp', identifiers: ['cs', 'csharp', 'c#'], source: 'source.cs' },
|
||||
{ name: 'fsharp', language: 'fsharp', identifiers: ['fs', 'fsharp', 'f#'], source: 'source.fsharp' },
|
||||
];
|
||||
|
||||
const fencedCodeBlockDefinition = (name, identifiers, sourceScope, language) => {
|
||||
if (!Array.isArray(sourceScope)) {
|
||||
sourceScope = [sourceScope];
|
||||
}
|
||||
|
||||
language = language || name
|
||||
|
||||
const scopes = sourceScope.map(scope =>
|
||||
`<dict>
|
||||
<key>include</key>
|
||||
<string>${scope}</string>
|
||||
</dict>`).join('\n');
|
||||
|
||||
return `<key>fenced_code_block_${name}</key>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>(^|\\G)(\\s*)(\`{3,}|~{3,})\\s*(?i:(${identifiers.join('|')})(\\s+[^\`~]*)?$)</string>
|
||||
<key>name</key>
|
||||
<string>markup.fenced_code.block.markdown</string>
|
||||
<key>end</key>
|
||||
<string>(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>3</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.markdown</string>
|
||||
</dict>
|
||||
<key>5</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>fenced_code.block.language</string>
|
||||
</dict>
|
||||
<key>6</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>fenced_code.block.language.attributes</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>endCaptures</key>
|
||||
<dict>
|
||||
<key>3</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.markdown</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>(^|\\G)(\\s*)(.*)</string>
|
||||
<key>while</key>
|
||||
<string>(^|\\G)(?!\\s*([\`~]{3,})\\s*$)</string>
|
||||
<key>contentName</key>
|
||||
<string>meta.embedded.block.${language}</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
${indent(4, scopes)}
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>`;
|
||||
};
|
||||
|
||||
const indent = (count, text) => {
|
||||
const indent = new Array(count + 1).join('\t');
|
||||
return text.replace(/^/gm, indent);
|
||||
};
|
||||
|
||||
const fencedCodeBlockInclude = (name) =>
|
||||
`<dict>
|
||||
<key>include</key>
|
||||
<string>#fenced_code_block_${name}</string>
|
||||
</dict>`;
|
||||
|
||||
|
||||
const fencedCodeBlockDefinitions = () =>
|
||||
languages
|
||||
.map(language => fencedCodeBlockDefinition(language.name, language.identifiers, language.source, language.language))
|
||||
.join('\n');
|
||||
|
||||
|
||||
|
||||
const fencedCodeBlockIncludes = () =>
|
||||
languages
|
||||
.map(language => fencedCodeBlockInclude(language.name))
|
||||
.join('\n');
|
||||
|
||||
|
||||
gulp.task('default', function () {
|
||||
gulp.src(['markdown.tmLanguage.base'])
|
||||
.pipe(replace('{{languageIncludes}}', indent(4, fencedCodeBlockIncludes())))
|
||||
.pipe(replace('{{languageDefinitions}}', indent(4, fencedCodeBlockDefinitions())))
|
||||
.pipe(rename('markdown.tmLanguage'))
|
||||
.pipe(gulp.dest('.'));
|
||||
});
|
||||
|
||||
gulp.task('embedded', function () {
|
||||
const out = {}
|
||||
for (const lang of languages.filter(x => x.language)) {
|
||||
out['meta.embedded.block.' +lang.language] = lang.language;
|
||||
}
|
||||
util.log(JSON.stringify(out, undefined, 4));
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
# h
|
||||
|
||||
<pre><code>
|
||||
# a
|
||||
</code></pre>
|
||||
|
||||
# h
|
||||
|
||||
<pre>
|
||||
# a
|
||||
a</pre>
|
||||
|
||||
# h
|
||||
@@ -1,106 +0,0 @@
|
||||
# Header 1 #
|
||||
## Header 2 ##
|
||||
### Header 3 ### (Hashes on right are optional)
|
||||
## Markdown plus h2 with a custom ID ## {#id-goes-here}
|
||||
[Link back to H2](#id-goes-here)
|
||||
|
||||
### Alternate heading styles:
|
||||
Alternate Header 1
|
||||
==================
|
||||
Alternate Header 2
|
||||
------------------
|
||||
|
||||
<!-- html madness -->
|
||||
<div class="custom-class" markdown="1">
|
||||
<div>
|
||||
nested div
|
||||
</div>
|
||||
<script type='text/x-koka'>
|
||||
function( x: int ) { return x*x; }
|
||||
</script>
|
||||
This is a div _with_ underscores
|
||||
and a & <b class="bold">bold</b> element.
|
||||
<style>
|
||||
body { font: "Consolas" }
|
||||
</style>
|
||||
</div>
|
||||
|
||||
* Bullet lists are easy too
|
||||
- Another one
|
||||
+ Another one
|
||||
|
||||
+ nested list
|
||||
|
||||
This is a paragraph, which is text surrounded by
|
||||
whitespace. Paragraphs can be on one
|
||||
line (or many), and can drone on for hours.
|
||||
|
||||
Now some inline markup like _italics_, **bold**,
|
||||
and `code()`. Note that underscores
|
||||
in_words_are ignored.
|
||||
|
||||
````application/json
|
||||
{ value: ["or with a mime type"] }
|
||||
````
|
||||
|
||||
> Blockquotes are like quoted text in email replies
|
||||
>> And, they can be nested
|
||||
|
||||
1. A numbered list
|
||||
> Block quotes in list
|
||||
2. Which is numbered
|
||||
3. With periods and a space
|
||||
|
||||
And now some code:
|
||||
|
||||
// Code is just text indented a bit
|
||||
which(is_easy) to_remember();
|
||||
|
||||
And a block
|
||||
|
||||
~~~
|
||||
// Markdown extra adds un-indented code blocks too
|
||||
|
||||
if (this_is_more_code == true && !indented) {
|
||||
// tild wrapped code blocks, also not indented
|
||||
}
|
||||
~~~
|
||||
|
||||
Text with
|
||||
two trailing spaces
|
||||
(on the right)
|
||||
can be used
|
||||
for things like poems
|
||||
|
||||
### Horizontal rules
|
||||
|
||||
* * * *
|
||||
****
|
||||
--------------------------
|
||||
|
||||

|
||||
|
||||
## Markdown plus tables ##
|
||||
|
||||
| Header | Header | Right |
|
||||
| ------ | ------ | -----: |
|
||||
| Cell | Cell | $10 |
|
||||
| Cell | Cell | $20 |
|
||||
|
||||
* Outer pipes on tables are optional
|
||||
* Colon used for alignment (right versus left)
|
||||
|
||||
## Markdown plus definition lists ##
|
||||
|
||||
Bottled water
|
||||
: $ 1.25
|
||||
: $ 1.55 (Large)
|
||||
|
||||
Milk
|
||||
Pop
|
||||
: $ 1.75
|
||||
|
||||
* Multiple definitions and terms are possible
|
||||
* Definitions can include multiple paragraphs too
|
||||
|
||||
*[ABBR]: Markdown plus abbreviations (produces an <abbr> tag)
|
||||
@@ -1,332 +0,0 @@
|
||||
[
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown",
|
||||
"r": {
|
||||
"dark_plus": "markup.heading: #569CD6",
|
||||
"light_plus": "markup.heading: #800000",
|
||||
"dark_vs": "markup.heading: #569CD6",
|
||||
"light_vs": "markup.heading: #800000",
|
||||
"hc_black": "markup.heading: #6796E6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.html.markdown markup.heading.markdown",
|
||||
"r": {
|
||||
"dark_plus": "markup.heading: #569CD6",
|
||||
"light_plus": "markup.heading: #800000",
|
||||
"dark_vs": "markup.heading: #569CD6",
|
||||
"light_vs": "markup.heading: #800000",
|
||||
"hc_black": "markup.heading: #6796E6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "h",
|
||||
"t": "text.html.markdown markup.heading.markdown entity.name.section.markdown",
|
||||
"r": {
|
||||
"dark_plus": "markup.heading: #569CD6",
|
||||
"light_plus": "markup.heading: #800000",
|
||||
"dark_vs": "markup.heading: #569CD6",
|
||||
"light_vs": "markup.heading: #800000",
|
||||
"hc_black": "markup.heading: #6796E6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "<",
|
||||
"t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html",
|
||||
"r": {
|
||||
"dark_plus": "punctuation.definition.tag: #808080",
|
||||
"light_plus": "punctuation.definition.tag: #800000",
|
||||
"dark_vs": "punctuation.definition.tag: #808080",
|
||||
"light_vs": "punctuation.definition.tag: #800000",
|
||||
"hc_black": "punctuation.definition.tag: #808080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "pre",
|
||||
"t": "text.html.markdown meta.tag.block.any.html entity.name.tag.block.any.html",
|
||||
"r": {
|
||||
"dark_plus": "entity.name.tag: #569CD6",
|
||||
"light_plus": "entity.name.tag: #800000",
|
||||
"dark_vs": "entity.name.tag: #569CD6",
|
||||
"light_vs": "entity.name.tag: #800000",
|
||||
"hc_black": "entity.name.tag: #569CD6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": ">",
|
||||
"t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html",
|
||||
"r": {
|
||||
"dark_plus": "punctuation.definition.tag: #808080",
|
||||
"light_plus": "punctuation.definition.tag: #800000",
|
||||
"dark_vs": "punctuation.definition.tag: #808080",
|
||||
"light_vs": "punctuation.definition.tag: #800000",
|
||||
"hc_black": "punctuation.definition.tag: #808080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "<",
|
||||
"t": "text.html.markdown meta.tag.inline.any.html punctuation.definition.tag.begin.html",
|
||||
"r": {
|
||||
"dark_plus": "punctuation.definition.tag: #808080",
|
||||
"light_plus": "punctuation.definition.tag: #800000",
|
||||
"dark_vs": "punctuation.definition.tag: #808080",
|
||||
"light_vs": "punctuation.definition.tag: #800000",
|
||||
"hc_black": "punctuation.definition.tag: #808080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "code",
|
||||
"t": "text.html.markdown meta.tag.inline.any.html entity.name.tag.inline.any.html",
|
||||
"r": {
|
||||
"dark_plus": "entity.name.tag: #569CD6",
|
||||
"light_plus": "entity.name.tag: #800000",
|
||||
"dark_vs": "entity.name.tag: #569CD6",
|
||||
"light_vs": "entity.name.tag: #800000",
|
||||
"hc_black": "entity.name.tag: #569CD6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": ">",
|
||||
"t": "text.html.markdown meta.tag.inline.any.html punctuation.definition.tag.end.html",
|
||||
"r": {
|
||||
"dark_plus": "punctuation.definition.tag: #808080",
|
||||
"light_plus": "punctuation.definition.tag: #800000",
|
||||
"dark_vs": "punctuation.definition.tag: #808080",
|
||||
"light_vs": "punctuation.definition.tag: #800000",
|
||||
"hc_black": "punctuation.definition.tag: #808080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "# a",
|
||||
"t": "text.html.markdown",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "</",
|
||||
"t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.any.html punctuation.definition.tag.begin.html",
|
||||
"r": {
|
||||
"dark_plus": "punctuation.definition.tag: #808080",
|
||||
"light_plus": "punctuation.definition.tag: #800000",
|
||||
"dark_vs": "punctuation.definition.tag: #808080",
|
||||
"light_vs": "punctuation.definition.tag: #800000",
|
||||
"hc_black": "punctuation.definition.tag: #808080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "code",
|
||||
"t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.any.html entity.name.tag.inline.any.html",
|
||||
"r": {
|
||||
"dark_plus": "entity.name.tag: #569CD6",
|
||||
"light_plus": "entity.name.tag: #800000",
|
||||
"dark_vs": "entity.name.tag: #569CD6",
|
||||
"light_vs": "entity.name.tag: #800000",
|
||||
"hc_black": "entity.name.tag: #569CD6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": ">",
|
||||
"t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.any.html punctuation.definition.tag.end.html",
|
||||
"r": {
|
||||
"dark_plus": "punctuation.definition.tag: #808080",
|
||||
"light_plus": "punctuation.definition.tag: #800000",
|
||||
"dark_vs": "punctuation.definition.tag: #808080",
|
||||
"light_vs": "punctuation.definition.tag: #800000",
|
||||
"hc_black": "punctuation.definition.tag: #808080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "</",
|
||||
"t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html",
|
||||
"r": {
|
||||
"dark_plus": "punctuation.definition.tag: #808080",
|
||||
"light_plus": "punctuation.definition.tag: #800000",
|
||||
"dark_vs": "punctuation.definition.tag: #808080",
|
||||
"light_vs": "punctuation.definition.tag: #800000",
|
||||
"hc_black": "punctuation.definition.tag: #808080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "pre",
|
||||
"t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html entity.name.tag.block.any.html",
|
||||
"r": {
|
||||
"dark_plus": "entity.name.tag: #569CD6",
|
||||
"light_plus": "entity.name.tag: #800000",
|
||||
"dark_vs": "entity.name.tag: #569CD6",
|
||||
"light_vs": "entity.name.tag: #800000",
|
||||
"hc_black": "entity.name.tag: #569CD6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": ">",
|
||||
"t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html punctuation.definition.tag.end.html",
|
||||
"r": {
|
||||
"dark_plus": "punctuation.definition.tag: #808080",
|
||||
"light_plus": "punctuation.definition.tag: #800000",
|
||||
"dark_vs": "punctuation.definition.tag: #808080",
|
||||
"light_vs": "punctuation.definition.tag: #800000",
|
||||
"hc_black": "punctuation.definition.tag: #808080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown",
|
||||
"r": {
|
||||
"dark_plus": "markup.heading: #569CD6",
|
||||
"light_plus": "markup.heading: #800000",
|
||||
"dark_vs": "markup.heading: #569CD6",
|
||||
"light_vs": "markup.heading: #800000",
|
||||
"hc_black": "markup.heading: #6796E6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.html.markdown markup.heading.markdown",
|
||||
"r": {
|
||||
"dark_plus": "markup.heading: #569CD6",
|
||||
"light_plus": "markup.heading: #800000",
|
||||
"dark_vs": "markup.heading: #569CD6",
|
||||
"light_vs": "markup.heading: #800000",
|
||||
"hc_black": "markup.heading: #6796E6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "h",
|
||||
"t": "text.html.markdown markup.heading.markdown entity.name.section.markdown",
|
||||
"r": {
|
||||
"dark_plus": "markup.heading: #569CD6",
|
||||
"light_plus": "markup.heading: #800000",
|
||||
"dark_vs": "markup.heading: #569CD6",
|
||||
"light_vs": "markup.heading: #800000",
|
||||
"hc_black": "markup.heading: #6796E6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "<",
|
||||
"t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html",
|
||||
"r": {
|
||||
"dark_plus": "punctuation.definition.tag: #808080",
|
||||
"light_plus": "punctuation.definition.tag: #800000",
|
||||
"dark_vs": "punctuation.definition.tag: #808080",
|
||||
"light_vs": "punctuation.definition.tag: #800000",
|
||||
"hc_black": "punctuation.definition.tag: #808080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "pre",
|
||||
"t": "text.html.markdown meta.tag.block.any.html entity.name.tag.block.any.html",
|
||||
"r": {
|
||||
"dark_plus": "entity.name.tag: #569CD6",
|
||||
"light_plus": "entity.name.tag: #800000",
|
||||
"dark_vs": "entity.name.tag: #569CD6",
|
||||
"light_vs": "entity.name.tag: #800000",
|
||||
"hc_black": "entity.name.tag: #569CD6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": ">",
|
||||
"t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html",
|
||||
"r": {
|
||||
"dark_plus": "punctuation.definition.tag: #808080",
|
||||
"light_plus": "punctuation.definition.tag: #800000",
|
||||
"dark_vs": "punctuation.definition.tag: #808080",
|
||||
"light_vs": "punctuation.definition.tag: #800000",
|
||||
"hc_black": "punctuation.definition.tag: #808080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "# a",
|
||||
"t": "text.html.markdown",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "a",
|
||||
"t": "text.html.markdown meta.paragraph.markdown",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "</",
|
||||
"t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html",
|
||||
"r": {
|
||||
"dark_plus": "punctuation.definition.tag: #808080",
|
||||
"light_plus": "punctuation.definition.tag: #800000",
|
||||
"dark_vs": "punctuation.definition.tag: #808080",
|
||||
"light_vs": "punctuation.definition.tag: #800000",
|
||||
"hc_black": "punctuation.definition.tag: #808080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "pre",
|
||||
"t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html entity.name.tag.block.any.html",
|
||||
"r": {
|
||||
"dark_plus": "entity.name.tag: #569CD6",
|
||||
"light_plus": "entity.name.tag: #800000",
|
||||
"dark_vs": "entity.name.tag: #569CD6",
|
||||
"light_vs": "entity.name.tag: #800000",
|
||||
"hc_black": "entity.name.tag: #569CD6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": ">",
|
||||
"t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html punctuation.definition.tag.end.html",
|
||||
"r": {
|
||||
"dark_plus": "punctuation.definition.tag: #808080",
|
||||
"light_plus": "punctuation.definition.tag: #800000",
|
||||
"dark_vs": "punctuation.definition.tag: #808080",
|
||||
"light_vs": "punctuation.definition.tag: #800000",
|
||||
"hc_black": "punctuation.definition.tag: #808080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown",
|
||||
"r": {
|
||||
"dark_plus": "markup.heading: #569CD6",
|
||||
"light_plus": "markup.heading: #800000",
|
||||
"dark_vs": "markup.heading: #569CD6",
|
||||
"light_vs": "markup.heading: #800000",
|
||||
"hc_black": "markup.heading: #6796E6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.html.markdown markup.heading.markdown",
|
||||
"r": {
|
||||
"dark_plus": "markup.heading: #569CD6",
|
||||
"light_plus": "markup.heading: #800000",
|
||||
"dark_vs": "markup.heading: #569CD6",
|
||||
"light_vs": "markup.heading: #800000",
|
||||
"hc_black": "markup.heading: #6796E6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "h",
|
||||
"t": "text.html.markdown markup.heading.markdown entity.name.section.markdown",
|
||||
"r": {
|
||||
"dark_plus": "markup.heading: #569CD6",
|
||||
"light_plus": "markup.heading: #800000",
|
||||
"dark_vs": "markup.heading: #569CD6",
|
||||
"light_vs": "markup.heading: #800000",
|
||||
"hc_black": "markup.heading: #6796E6"
|
||||
}
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,8 @@
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"strict": true
|
||||
"strict": true,
|
||||
"alwaysStrict": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user