mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 09:08:48 +01:00
Emmet refactor reflect CSS and update image commands (#113550)
This commit is contained in:
@@ -3,20 +3,20 @@
|
|||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { Range, window, TextEditor } from 'vscode';
|
import { window, TextEditor } from 'vscode';
|
||||||
import { getCssPropertyFromRule, getCssPropertyFromDocument } from './util';
|
import { getCssPropertyFromRule, getCssPropertyFromDocument, offsetRangeToVsRange } from './util';
|
||||||
import { Property, Rule } from 'EmmetNode';
|
import { Property, Rule } from 'EmmetFlatNode';
|
||||||
|
|
||||||
const vendorPrefixes = ['-webkit-', '-moz-', '-ms-', '-o-', ''];
|
const vendorPrefixes = ['-webkit-', '-moz-', '-ms-', '-o-', ''];
|
||||||
|
|
||||||
export function reflectCssValue(): Thenable<boolean> | undefined {
|
export function reflectCssValue(): Thenable<boolean> | undefined {
|
||||||
let editor = window.activeTextEditor;
|
const editor = window.activeTextEditor;
|
||||||
if (!editor) {
|
if (!editor) {
|
||||||
window.showInformationMessage('No editor is active.');
|
window.showInformationMessage('No editor is active.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = getCssPropertyFromDocument(editor, editor.selection.active);
|
const node = getCssPropertyFromDocument(editor, editor.selection.active);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -45,10 +45,11 @@ function updateCSSNode(editor: TextEditor, property: Property): Thenable<boolean
|
|||||||
if (prefix === currentPrefix) {
|
if (prefix === currentPrefix) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let vendorProperty = getCssPropertyFromRule(rule, prefix + propertyName);
|
const vendorProperty = getCssPropertyFromRule(rule, prefix + propertyName);
|
||||||
if (vendorProperty) {
|
if (vendorProperty) {
|
||||||
builder.replace(new Range(vendorProperty.valueToken.start, vendorProperty.valueToken.end), propertyValue);
|
const rangeToReplace = offsetRangeToVsRange(editor.document, vendorProperty.valueToken.start, vendorProperty.valueToken.end);
|
||||||
|
builder.replace(rangeToReplace, propertyValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,148 +3,147 @@
|
|||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
// import 'mocha';
|
import 'mocha';
|
||||||
// import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
// import { Selection } from 'vscode';
|
import { Selection } from 'vscode';
|
||||||
// import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
import { withRandomFileEditor, closeAllEditors } from './testUtils';
|
||||||
// import { updateImageSize } from '../updateImageSize';
|
import { updateImageSize } from '../updateImageSize';
|
||||||
|
|
||||||
// suite('Tests for Emmet actions on html tags', () => {
|
suite('Tests for Emmet actions on html tags', () => {
|
||||||
// teardown(closeAllEditors);
|
teardown(closeAllEditors);
|
||||||
|
|
||||||
// test('update image css with multiple cursors in css file', () => {
|
test('update image css with multiple cursors in css file', () => {
|
||||||
// const cssContents = `
|
const cssContents = `
|
||||||
// .one {
|
.one {
|
||||||
// margin: 10px;
|
margin: 10px;
|
||||||
// padding: 10px;
|
padding: 10px;
|
||||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||||
// }
|
}
|
||||||
// .two {
|
.two {
|
||||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||||
// height: 42px;
|
height: 42px;
|
||||||
// }
|
}
|
||||||
// .three {
|
.three {
|
||||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||||
// width: 42px;
|
width: 42px;
|
||||||
// }
|
}
|
||||||
// `;
|
`;
|
||||||
// const expectedContents = `
|
const expectedContents = `
|
||||||
// .one {
|
.one {
|
||||||
// margin: 10px;
|
margin: 10px;
|
||||||
// padding: 10px;
|
padding: 10px;
|
||||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||||
// width: 32px;
|
width: 1024px;
|
||||||
// height: 32px;
|
height: 1024px;
|
||||||
// }
|
}
|
||||||
// .two {
|
.two {
|
||||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||||
// width: 32px;
|
width: 1024px;
|
||||||
// height: 32px;
|
height: 1024px;
|
||||||
// }
|
}
|
||||||
// .three {
|
.three {
|
||||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||||
// height: 32px;
|
height: 1024px;
|
||||||
// width: 32px;
|
width: 1024px;
|
||||||
// }
|
}
|
||||||
// `;
|
`;
|
||||||
// return withRandomFileEditor(cssContents, 'css', (editor, doc) => {
|
return withRandomFileEditor(cssContents, 'css', (editor, doc) => {
|
||||||
// editor.selections = [
|
editor.selections = [
|
||||||
// new Selection(4, 50, 4, 50),
|
new Selection(4, 50, 4, 50),
|
||||||
// new Selection(7, 50, 7, 50),
|
new Selection(7, 50, 7, 50),
|
||||||
// new Selection(11, 50, 11, 50)
|
new Selection(11, 50, 11, 50)
|
||||||
// ];
|
];
|
||||||
|
|
||||||
// return updateImageSize()!.then(() => {
|
return updateImageSize()!.then(() => {
|
||||||
// assert.equal(doc.getText(), expectedContents);
|
assert.equal(doc.getText(), expectedContents);
|
||||||
// return Promise.resolve();
|
return Promise.resolve();
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
|
|
||||||
// test('update image size in css in html file with multiple cursors', () => {
|
test('update image size in css in html file with multiple cursors', () => {
|
||||||
// const htmlWithCssContents = `
|
const htmlWithCssContents = `
|
||||||
// <html>
|
<html>
|
||||||
// <style>
|
<style>
|
||||||
// .one {
|
.one {
|
||||||
// margin: 10px;
|
margin: 10px;
|
||||||
// padding: 10px;
|
padding: 10px;
|
||||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||||
// }
|
}
|
||||||
// .two {
|
.two {
|
||||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||||
// height: 42px;
|
height: 42px;
|
||||||
// }
|
}
|
||||||
// .three {
|
.three {
|
||||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||||
// width: 42px;
|
width: 42px;
|
||||||
// }
|
}
|
||||||
// </style>
|
</style>
|
||||||
// </html>
|
</html>
|
||||||
// `;
|
`;
|
||||||
// const expectedContents = `
|
const expectedContents = `
|
||||||
// <html>
|
<html>
|
||||||
// <style>
|
<style>
|
||||||
// .one {
|
.one {
|
||||||
// margin: 10px;
|
margin: 10px;
|
||||||
// padding: 10px;
|
padding: 10px;
|
||||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||||
// width: 32px;
|
width: 1024px;
|
||||||
// height: 32px;
|
height: 1024px;
|
||||||
// }
|
}
|
||||||
// .two {
|
.two {
|
||||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||||
// width: 32px;
|
width: 1024px;
|
||||||
// height: 32px;
|
height: 1024px;
|
||||||
// }
|
}
|
||||||
// .three {
|
.three {
|
||||||
// background-image: url(https://github.com/microsoft/vscode/blob/master/resources/linux/code.png);
|
background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png);
|
||||||
// height: 32px;
|
height: 1024px;
|
||||||
// width: 32px;
|
width: 1024px;
|
||||||
// }
|
}
|
||||||
// </style>
|
</style>
|
||||||
// </html>
|
</html>
|
||||||
// `;
|
`;
|
||||||
// return withRandomFileEditor(htmlWithCssContents, 'html', (editor, doc) => {
|
return withRandomFileEditor(htmlWithCssContents, 'html', (editor, doc) => {
|
||||||
// editor.selections = [
|
editor.selections = [
|
||||||
// new Selection(6, 50, 6, 50),
|
new Selection(6, 50, 6, 50),
|
||||||
// new Selection(9, 50, 9, 50),
|
new Selection(9, 50, 9, 50),
|
||||||
// new Selection(13, 50, 13, 50)
|
new Selection(13, 50, 13, 50)
|
||||||
// ];
|
];
|
||||||
|
|
||||||
// return updateImageSize()!.then(() => {
|
return updateImageSize()!.then(() => {
|
||||||
// assert.equal(doc.getText(), expectedContents);
|
assert.equal(doc.getText(), expectedContents);
|
||||||
// return Promise.resolve();
|
return Promise.resolve();
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
|
|
||||||
// test('update image size in img tag in html file with multiple cursors', () => {
|
test('update image size in img tag in html file with multiple cursors', () => {
|
||||||
// const htmlwithimgtag = `
|
const htmlwithimgtag = `
|
||||||
// <html>
|
<html>
|
||||||
// <img id="one" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" />
|
<img id="one" src="https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png" />
|
||||||
// <img id="two" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" width="56" />
|
<img id="two" src="https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png" width="56" />
|
||||||
// <img id="three" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" height="56" />
|
<img id="three" src="https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png" height="56" />
|
||||||
// </html>
|
</html>
|
||||||
// `;
|
`;
|
||||||
// const expectedContents = `
|
const expectedContents = `
|
||||||
// <html>
|
<html>
|
||||||
// <img id="one" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" width="32" height="32" />
|
<img id="one" src="https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png" width="1024" height="1024" />
|
||||||
// <img id="two" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" width="32" height="32" />
|
<img id="two" src="https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png" width="1024" height="1024" />
|
||||||
// <img id="three" src="https://github.com/microsoft/vscode/blob/master/resources/linux/code.png" height="32" width="32" />
|
<img id="three" src="https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png" height="1024" width="1024" />
|
||||||
// </html>
|
</html>
|
||||||
// `;
|
`;
|
||||||
// return withRandomFileEditor(htmlwithimgtag, 'html', (editor, doc) => {
|
return withRandomFileEditor(htmlwithimgtag, 'html', (editor, doc) => {
|
||||||
// editor.selections = [
|
editor.selections = [
|
||||||
// new Selection(2, 50, 2, 50),
|
new Selection(2, 50, 2, 50),
|
||||||
// new Selection(3, 50, 3, 50),
|
new Selection(3, 50, 3, 50),
|
||||||
// new Selection(4, 50, 4, 50)
|
new Selection(4, 50, 4, 50)
|
||||||
// ];
|
];
|
||||||
|
|
||||||
// return updateImageSize()!.then(() => {
|
return updateImageSize()!.then(() => {
|
||||||
// assert.equal(doc.getText(), expectedContents);
|
assert.equal(doc.getText(), expectedContents);
|
||||||
// return Promise.resolve();
|
return Promise.resolve();
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
|
});
|
||||||
// });
|
|
||||||
|
|||||||
+11
-1
@@ -18,7 +18,7 @@ declare module 'EmmetFlatNode' {
|
|||||||
export interface Token {
|
export interface Token {
|
||||||
start: number
|
start: number
|
||||||
end: number
|
end: number
|
||||||
stream: string
|
stream: BufferStream
|
||||||
toString(): string
|
toString(): string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,4 +76,14 @@ declare module 'EmmetFlatNode' {
|
|||||||
export interface Stylesheet extends Node {
|
export interface Stylesheet extends Node {
|
||||||
comments: Token[]
|
comments: Token[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BufferStream {
|
||||||
|
peek(): number
|
||||||
|
next(): number
|
||||||
|
backUp(n: number): number
|
||||||
|
current(): string
|
||||||
|
substring(from: number, to: number): string
|
||||||
|
eat(match: any): boolean
|
||||||
|
eatWhile(match: any): boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,26 +5,26 @@
|
|||||||
|
|
||||||
// Based on @sergeche's work on the emmet plugin for atom
|
// Based on @sergeche's work on the emmet plugin for atom
|
||||||
|
|
||||||
import { TextEditor, Range, Position, window, TextEdit } from 'vscode';
|
import { TextEditor, Position, window, TextEdit } from 'vscode';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { getImageSize } from './imageSizeHelper';
|
import { getImageSize } from './imageSizeHelper';
|
||||||
import { parseDocument, getNode, iterateCSSToken, getCssPropertyFromRule, isStyleSheet, validate } from './util';
|
import { getFlatNode, iterateCSSToken, getCssPropertyFromRule, isStyleSheet, validate, offsetRangeToVsRange } from './util';
|
||||||
import { HtmlNode, CssToken, HtmlToken, Attribute, Property } from 'EmmetNode';
|
import { HtmlNode, CssToken, HtmlToken, Attribute, Property } from 'EmmetFlatNode';
|
||||||
import { locateFile } from './locateFile';
|
import { locateFile } from './locateFile';
|
||||||
import parseStylesheet from '@emmetio/css-parser';
|
import parseStylesheet from '@emmetio/css-parser';
|
||||||
import { DocumentStreamReader } from './bufferStream';
|
import { getRootNode } from './parseDocument';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates size of context image in given editor
|
* Updates size of context image in given editor
|
||||||
*/
|
*/
|
||||||
export function updateImageSize() {
|
export function updateImageSize(): Promise<boolean> | undefined {
|
||||||
if (!validate() || !window.activeTextEditor) {
|
if (!validate() || !window.activeTextEditor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const editor = window.activeTextEditor;
|
const editor = window.activeTextEditor;
|
||||||
|
|
||||||
let allUpdatesPromise = editor.selections.reverse().map(selection => {
|
const allUpdatesPromise = editor.selections.reverse().map(selection => {
|
||||||
let position = selection.isReversed ? selection.active : selection.anchor;
|
const position = selection.isReversed ? selection.active : selection.anchor;
|
||||||
if (!isStyleSheet(editor.document.languageId)) {
|
if (!isStyleSheet(editor.document.languageId)) {
|
||||||
return updateImageSizeHTML(editor, position);
|
return updateImageSizeHTML(editor, position);
|
||||||
} else {
|
} else {
|
||||||
@@ -71,15 +71,19 @@ function updateImageSizeHTML(editor: TextEditor, position: Position): Promise<Te
|
|||||||
|
|
||||||
function updateImageSizeStyleTag(editor: TextEditor, position: Position): Promise<TextEdit[]> {
|
function updateImageSizeStyleTag(editor: TextEditor, position: Position): Promise<TextEdit[]> {
|
||||||
const getPropertyInsiderStyleTag = (editor: TextEditor): Property | null => {
|
const getPropertyInsiderStyleTag = (editor: TextEditor): Property | null => {
|
||||||
const rootNode = parseDocument(editor.document);
|
const document = editor.document;
|
||||||
const currentNode = <HtmlNode>getNode(rootNode, position, true);
|
const rootNode = getRootNode(document, true);
|
||||||
|
const offset = document.offsetAt(position);
|
||||||
|
const currentNode = <HtmlNode>getFlatNode(rootNode, offset, true);
|
||||||
if (currentNode && currentNode.name === 'style'
|
if (currentNode && currentNode.name === 'style'
|
||||||
&& currentNode.open.end.isBefore(position)
|
&& currentNode.open && currentNode.close
|
||||||
&& currentNode.close.start.isAfter(position)) {
|
&& currentNode.open.end < offset
|
||||||
let buffer = new DocumentStreamReader(editor.document, currentNode.open.end, new Range(currentNode.open.end, currentNode.close.start));
|
&& currentNode.close.start > offset) {
|
||||||
let rootNode = parseStylesheet(buffer);
|
const buffer = ' '.repeat(currentNode.open.end) +
|
||||||
const node = getNode(rootNode, position, true);
|
document.getText().substring(currentNode.open.end, currentNode.close.start);
|
||||||
return (node && node.type === 'property') ? <Property>node : null;
|
const innerRootNode = parseStylesheet(buffer);
|
||||||
|
const innerNode = getFlatNode(innerRootNode, offset, true);
|
||||||
|
return (innerNode && innerNode.type === 'property') ? <Property>innerNode : null;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
@@ -96,7 +100,7 @@ function updateImageSizeCSSFile(editor: TextEditor, position: Position): Promise
|
|||||||
*/
|
*/
|
||||||
function updateImageSizeCSS(editor: TextEditor, position: Position, fetchNode: (editor: TextEditor, position: Position) => Property | null): Promise<TextEdit[]> {
|
function updateImageSizeCSS(editor: TextEditor, position: Position, fetchNode: (editor: TextEditor, position: Position) => Property | null): Promise<TextEdit[]> {
|
||||||
const node = fetchNode(editor, position);
|
const node = fetchNode(editor, position);
|
||||||
const src = node && getImageSrcCSS(node, position);
|
const src = node && getImageSrcCSS(editor, node, position);
|
||||||
|
|
||||||
if (!src) {
|
if (!src) {
|
||||||
return Promise.reject(new Error('No valid image source'));
|
return Promise.reject(new Error('No valid image source'));
|
||||||
@@ -108,7 +112,7 @@ function updateImageSizeCSS(editor: TextEditor, position: Position, fetchNode: (
|
|||||||
// since this action is asynchronous, we have to ensure that editor wasn’t
|
// since this action is asynchronous, we have to ensure that editor wasn’t
|
||||||
// changed and user didn’t moved caret outside <img> node
|
// changed and user didn’t moved caret outside <img> node
|
||||||
const prop = fetchNode(editor, position);
|
const prop = fetchNode(editor, position);
|
||||||
if (prop && getImageSrcCSS(prop, position) === src) {
|
if (prop && getImageSrcCSS(editor, prop, position) === src) {
|
||||||
return updateCSSNode(editor, prop, size.width, size.height);
|
return updateCSSNode(editor, prop, size.width, size.height);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
@@ -121,8 +125,10 @@ function updateImageSizeCSS(editor: TextEditor, position: Position, fetchNode: (
|
|||||||
* be found
|
* be found
|
||||||
*/
|
*/
|
||||||
function getImageHTMLNode(editor: TextEditor, position: Position): HtmlNode | null {
|
function getImageHTMLNode(editor: TextEditor, position: Position): HtmlNode | null {
|
||||||
const rootNode = parseDocument(editor.document);
|
const document = editor.document;
|
||||||
const node = <HtmlNode>getNode(rootNode, position, true);
|
const rootNode = getRootNode(document, true);
|
||||||
|
const offset = document.offsetAt(position);
|
||||||
|
const node = <HtmlNode>getFlatNode(rootNode, offset, true);
|
||||||
|
|
||||||
return node && node.name.toLowerCase() === 'img' ? node : null;
|
return node && node.name.toLowerCase() === 'img' ? node : null;
|
||||||
}
|
}
|
||||||
@@ -132,8 +138,10 @@ function getImageHTMLNode(editor: TextEditor, position: Position): HtmlNode | nu
|
|||||||
* be found
|
* be found
|
||||||
*/
|
*/
|
||||||
function getImageCSSNode(editor: TextEditor, position: Position): Property | null {
|
function getImageCSSNode(editor: TextEditor, position: Position): Property | null {
|
||||||
const rootNode = parseDocument(editor.document);
|
const document = editor.document;
|
||||||
const node = getNode(rootNode, position, true);
|
const rootNode = getRootNode(document, true);
|
||||||
|
const offset = document.offsetAt(position);
|
||||||
|
const node = getFlatNode(rootNode, offset, true);
|
||||||
return node && node.type === 'property' ? <Property>node : null;
|
return node && node.type === 'property' ? <Property>node : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,11 +160,11 @@ function getImageSrcHTML(node: HtmlNode): string | undefined {
|
|||||||
/**
|
/**
|
||||||
* Returns image source from given `url()` token
|
* Returns image source from given `url()` token
|
||||||
*/
|
*/
|
||||||
function getImageSrcCSS(node: Property | undefined, position: Position): string | undefined {
|
function getImageSrcCSS(editor: TextEditor, node: Property | undefined, position: Position): string | undefined {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const urlToken = findUrlToken(node, position);
|
const urlToken = findUrlToken(editor, node, position);
|
||||||
if (!urlToken) {
|
if (!urlToken) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -174,7 +182,12 @@ function getImageSrcCSS(node: Property | undefined, position: Position): string
|
|||||||
* Updates size of given HTML node
|
* Updates size of given HTML node
|
||||||
*/
|
*/
|
||||||
function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height: number): TextEdit[] {
|
function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height: number): TextEdit[] {
|
||||||
|
const document = editor.document;
|
||||||
const srcAttr = getAttribute(node, 'src');
|
const srcAttr = getAttribute(node, 'src');
|
||||||
|
if (!srcAttr) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const widthAttr = getAttribute(node, 'width');
|
const widthAttr = getAttribute(node, 'width');
|
||||||
const heightAttr = getAttribute(node, 'height');
|
const heightAttr = getAttribute(node, 'height');
|
||||||
const quote = getAttributeQuote(editor, srcAttr);
|
const quote = getAttributeQuote(editor, srcAttr);
|
||||||
@@ -186,15 +199,15 @@ function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height
|
|||||||
if (!widthAttr) {
|
if (!widthAttr) {
|
||||||
textToAdd += ` width=${quote}${width}${quote}`;
|
textToAdd += ` width=${quote}${width}${quote}`;
|
||||||
} else {
|
} else {
|
||||||
edits.push(new TextEdit(new Range(widthAttr.value.start, widthAttr.value.end), String(width)));
|
edits.push(new TextEdit(offsetRangeToVsRange(document, widthAttr.value.start, widthAttr.value.end), String(width)));
|
||||||
}
|
}
|
||||||
if (!heightAttr) {
|
if (!heightAttr) {
|
||||||
textToAdd += ` height=${quote}${height}${quote}`;
|
textToAdd += ` height=${quote}${height}${quote}`;
|
||||||
} else {
|
} else {
|
||||||
edits.push(new TextEdit(new Range(heightAttr.value.start, heightAttr.value.end), String(height)));
|
edits.push(new TextEdit(offsetRangeToVsRange(document, heightAttr.value.start, heightAttr.value.end), String(height)));
|
||||||
}
|
}
|
||||||
if (textToAdd) {
|
if (textToAdd) {
|
||||||
edits.push(new TextEdit(new Range(endOfAttributes, endOfAttributes), textToAdd));
|
edits.push(new TextEdit(offsetRangeToVsRange(document, endOfAttributes, endOfAttributes), textToAdd));
|
||||||
}
|
}
|
||||||
|
|
||||||
return edits;
|
return edits;
|
||||||
@@ -204,6 +217,7 @@ function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height
|
|||||||
* Updates size of given CSS rule
|
* Updates size of given CSS rule
|
||||||
*/
|
*/
|
||||||
function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, height: number): TextEdit[] {
|
function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, height: number): TextEdit[] {
|
||||||
|
const document = editor.document;
|
||||||
const rule = srcProp.parent;
|
const rule = srcProp.parent;
|
||||||
const widthProp = getCssPropertyFromRule(rule, 'width');
|
const widthProp = getCssPropertyFromRule(rule, 'width');
|
||||||
const heightProp = getCssPropertyFromRule(rule, 'height');
|
const heightProp = getCssPropertyFromRule(rule, 'height');
|
||||||
@@ -214,22 +228,22 @@ function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, hei
|
|||||||
|
|
||||||
let edits: TextEdit[] = [];
|
let edits: TextEdit[] = [];
|
||||||
if (!srcProp.terminatorToken) {
|
if (!srcProp.terminatorToken) {
|
||||||
edits.push(new TextEdit(new Range(srcProp.end, srcProp.end), ';'));
|
edits.push(new TextEdit(offsetRangeToVsRange(document, srcProp.end, srcProp.end), ';'));
|
||||||
}
|
}
|
||||||
|
|
||||||
let textToAdd = '';
|
let textToAdd = '';
|
||||||
if (!widthProp) {
|
if (!widthProp) {
|
||||||
textToAdd += `${before}width${separator}${width}px;`;
|
textToAdd += `${before}width${separator}${width}px;`;
|
||||||
} else {
|
} else {
|
||||||
edits.push(new TextEdit(new Range(widthProp.valueToken.start, widthProp.valueToken.end), `${width}px`));
|
edits.push(new TextEdit(offsetRangeToVsRange(document, widthProp.valueToken.start, widthProp.valueToken.end), `${width}px`));
|
||||||
}
|
}
|
||||||
if (!heightProp) {
|
if (!heightProp) {
|
||||||
textToAdd += `${before}height${separator}${height}px;`;
|
textToAdd += `${before}height${separator}${height}px;`;
|
||||||
} else {
|
} else {
|
||||||
edits.push(new TextEdit(new Range(heightProp.valueToken.start, heightProp.valueToken.end), `${height}px`));
|
edits.push(new TextEdit(offsetRangeToVsRange(document, heightProp.valueToken.start, heightProp.valueToken.end), `${height}px`));
|
||||||
}
|
}
|
||||||
if (textToAdd) {
|
if (textToAdd) {
|
||||||
edits.push(new TextEdit(new Range(srcProp.end, srcProp.end), textToAdd));
|
edits.push(new TextEdit(offsetRangeToVsRange(document, srcProp.end, srcProp.end), textToAdd));
|
||||||
}
|
}
|
||||||
|
|
||||||
return edits;
|
return edits;
|
||||||
@@ -238,9 +252,9 @@ function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, hei
|
|||||||
/**
|
/**
|
||||||
* Returns attribute object with `attrName` name from given HTML node
|
* Returns attribute object with `attrName` name from given HTML node
|
||||||
*/
|
*/
|
||||||
function getAttribute(node: HtmlNode, attrName: string): Attribute {
|
function getAttribute(node: HtmlNode, attrName: string): Attribute | undefined {
|
||||||
attrName = attrName.toLowerCase();
|
attrName = attrName.toLowerCase();
|
||||||
return node && (node.open as any).attributes.find((attr: any) => attr.name.value.toLowerCase() === attrName);
|
return node && node.attributes.find(attr => attr.name.toString().toLowerCase() === attrName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -248,18 +262,20 @@ function getAttribute(node: HtmlNode, attrName: string): Attribute {
|
|||||||
* string if attribute wasn’t quoted
|
* string if attribute wasn’t quoted
|
||||||
|
|
||||||
*/
|
*/
|
||||||
function getAttributeQuote(editor: TextEditor, attr: any): string {
|
function getAttributeQuote(editor: TextEditor, attr: Attribute): string {
|
||||||
const range = new Range(attr.value ? attr.value.end : attr.end, attr.end);
|
const begin = attr.value ? attr.value.end : attr.end;
|
||||||
return range.isEmpty ? '' : editor.document.getText(range);
|
const end = attr.end;
|
||||||
|
return begin === end ? '' : editor.document.getText().substring(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds 'url' token for given `pos` point in given CSS property `node`
|
* Finds 'url' token for given `pos` point in given CSS property `node`
|
||||||
*/
|
*/
|
||||||
function findUrlToken(node: Property, pos: Position): CssToken | undefined {
|
function findUrlToken(editor: TextEditor, node: Property, pos: Position): CssToken | undefined {
|
||||||
|
const offset = editor.document.offsetAt(pos);
|
||||||
for (let i = 0, il = (node as any).parsedValue.length, url; i < il; i++) {
|
for (let i = 0, il = (node as any).parsedValue.length, url; i < il; i++) {
|
||||||
iterateCSSToken((node as any).parsedValue[i], (token: CssToken) => {
|
iterateCSSToken((node as any).parsedValue[i], (token: CssToken) => {
|
||||||
if (token.type === 'url' && token.start.isBeforeOrEqual(pos) && token.end.isAfterOrEqual(pos)) {
|
if (token.type === 'url' && token.start <= offset && token.end >= offset) {
|
||||||
url = token;
|
url = token;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -279,9 +295,9 @@ function findUrlToken(node: Property, pos: Position): CssToken | undefined {
|
|||||||
function getPropertyDelimitor(editor: TextEditor, node: Property): string {
|
function getPropertyDelimitor(editor: TextEditor, node: Property): string {
|
||||||
let anchor;
|
let anchor;
|
||||||
if (anchor = (node.previousSibling || node.parent.contentStartToken)) {
|
if (anchor = (node.previousSibling || node.parent.contentStartToken)) {
|
||||||
return editor.document.getText(new Range(anchor.end, node.start));
|
return editor.document.getText().substring(anchor.end, node.start);
|
||||||
} else if (anchor = (node.nextSibling || node.parent.contentEndToken)) {
|
} else if (anchor = (node.nextSibling || node.parent.contentEndToken)) {
|
||||||
return editor.document.getText(new Range(node.end, anchor.start));
|
return editor.document.getText().substring(node.end, anchor.start);
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
|
|||||||
@@ -6,11 +6,12 @@
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import parse from '@emmetio/html-matcher';
|
import parse from '@emmetio/html-matcher';
|
||||||
import parseStylesheet from '@emmetio/css-parser';
|
import parseStylesheet from '@emmetio/css-parser';
|
||||||
import { Node, HtmlNode, CssToken, Property, Rule, Stylesheet } from 'EmmetNode';
|
import { Node, HtmlNode, Stylesheet } from 'EmmetNode';
|
||||||
import { Node as FlatNode, HtmlNode as HtmlFlatNode } from 'EmmetFlatNode';
|
import { Node as FlatNode, HtmlNode as HtmlFlatNode, Property as FlatProperty, Rule as FlatRule, CssToken as FlatCssToken } from 'EmmetFlatNode';
|
||||||
import { DocumentStreamReader } from './bufferStream';
|
import { DocumentStreamReader } from './bufferStream';
|
||||||
import * as EmmetHelper from 'vscode-emmet-helper';
|
import * as EmmetHelper from 'vscode-emmet-helper';
|
||||||
import { TextDocument as LSTextDocument } from 'vscode-languageserver-textdocument';
|
import { TextDocument as LSTextDocument } from 'vscode-languageserver-textdocument';
|
||||||
|
import { getRootNode } from './parseDocument';
|
||||||
|
|
||||||
let _emmetHelper: typeof EmmetHelper;
|
let _emmetHelper: typeof EmmetHelper;
|
||||||
let _currentExtensionsPath: string | undefined = undefined;
|
let _currentExtensionsPath: string | undefined = undefined;
|
||||||
@@ -652,7 +653,7 @@ export function getEmmetConfiguration(syntax: string) {
|
|||||||
* Itereates by each child, as well as nested child's children, in their order
|
* Itereates by each child, as well as nested child's children, in their order
|
||||||
* and invokes `fn` for each. If `fn` function returns `false`, iteration stops
|
* and invokes `fn` for each. If `fn` function returns `false`, iteration stops
|
||||||
*/
|
*/
|
||||||
export function iterateCSSToken(token: CssToken, fn: (x: any) => any): boolean {
|
export function iterateCSSToken(token: FlatCssToken, fn: (x: any) => any): boolean {
|
||||||
for (let i = 0, il = token.size; i < il; i++) {
|
for (let i = 0, il = token.size; i < il; i++) {
|
||||||
if (fn(token.item(i)) === false || iterateCSSToken(token.item(i), fn) === false) {
|
if (fn(token.item(i)) === false || iterateCSSToken(token.item(i), fn) === false) {
|
||||||
return false;
|
return false;
|
||||||
@@ -664,31 +665,35 @@ export function iterateCSSToken(token: CssToken, fn: (x: any) => any): boolean {
|
|||||||
/**
|
/**
|
||||||
* Returns `name` CSS property from given `rule`
|
* Returns `name` CSS property from given `rule`
|
||||||
*/
|
*/
|
||||||
export function getCssPropertyFromRule(rule: Rule, name: string): Property | undefined {
|
export function getCssPropertyFromRule(rule: FlatRule, name: string): FlatProperty | undefined {
|
||||||
return rule.children.find(node => node.type === 'property' && node.name === name) as Property;
|
return rule.children.find(node => node.type === 'property' && node.name === name) as FlatProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns css property under caret in given editor or `null` if such node cannot
|
* Returns css property under caret in given editor or `null` if such node cannot
|
||||||
* be found
|
* be found
|
||||||
*/
|
*/
|
||||||
export function getCssPropertyFromDocument(editor: vscode.TextEditor, position: vscode.Position): Property | null {
|
export function getCssPropertyFromDocument(editor: vscode.TextEditor, position: vscode.Position): FlatProperty | null {
|
||||||
const rootNode = parseDocument(editor.document);
|
const document = editor.document;
|
||||||
const node = getNode(rootNode, position, true);
|
const rootNode = getRootNode(document, true);
|
||||||
|
const offset = document.offsetAt(position);
|
||||||
|
const node = getFlatNode(rootNode, offset, true);
|
||||||
|
|
||||||
if (isStyleSheet(editor.document.languageId)) {
|
if (isStyleSheet(editor.document.languageId)) {
|
||||||
return node && node.type === 'property' ? <Property>node : null;
|
return node && node.type === 'property' ? <FlatProperty>node : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let htmlNode = <HtmlNode>node;
|
const htmlNode = <HtmlFlatNode>node;
|
||||||
if (htmlNode
|
if (htmlNode
|
||||||
&& htmlNode.name === 'style'
|
&& htmlNode.name === 'style'
|
||||||
&& htmlNode.open.end.isBefore(position)
|
&& htmlNode.open && htmlNode.close
|
||||||
&& htmlNode.close.start.isAfter(position)) {
|
&& htmlNode.open.end < offset
|
||||||
let buffer = new DocumentStreamReader(editor.document, htmlNode.start, new vscode.Range(htmlNode.start, htmlNode.end));
|
&& htmlNode.close.start > offset) {
|
||||||
let rootNode = parseStylesheet(buffer);
|
const buffer = ' '.repeat(htmlNode.start) +
|
||||||
const node = getNode(rootNode, position, true);
|
document.getText().substring(htmlNode.start, htmlNode.end);
|
||||||
return (node && node.type === 'property') ? <Property>node : null;
|
const innerRootNode = parseStylesheet(buffer);
|
||||||
|
const innerNode = getFlatNode(innerRootNode, offset, true);
|
||||||
|
return (innerNode && innerNode.type === 'property') ? <FlatProperty>innerNode : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -705,9 +710,7 @@ export function getEmbeddedCssNodeIfAny(document: vscode.TextDocument, currentNo
|
|||||||
if (innerRange && innerRange.contains(position)) {
|
if (innerRange && innerRange.contains(position)) {
|
||||||
if (currentHtmlNode.name === 'style'
|
if (currentHtmlNode.name === 'style'
|
||||||
&& currentHtmlNode.open.end.isBefore(position)
|
&& currentHtmlNode.open.end.isBefore(position)
|
||||||
&& currentHtmlNode.close.start.isAfter(position)
|
&& currentHtmlNode.close.start.isAfter(position)) {
|
||||||
|
|
||||||
) {
|
|
||||||
let buffer = new DocumentStreamReader(document, currentHtmlNode.open.end, new vscode.Range(currentHtmlNode.open.end, currentHtmlNode.close.start));
|
let buffer = new DocumentStreamReader(document, currentHtmlNode.open.end, new vscode.Range(currentHtmlNode.open.end, currentHtmlNode.close.start));
|
||||||
return parseStylesheet(buffer);
|
return parseStylesheet(buffer);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user