Skip to content

Commit

Permalink
Emmet Update Image Size command
Browse files Browse the repository at this point in the history
  • Loading branch information
ramya-rao-a committed Jul 23, 2017
1 parent caa0ef4 commit 0bee43b
Show file tree
Hide file tree
Showing 7 changed files with 551 additions and 2 deletions.
3 changes: 2 additions & 1 deletion extensions/emmet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"@emmetio/css-parser": "^0.3.0",
"@emmetio/math-expression": "^0.1.1",
"vscode-emmet-helper":"0.0.28",
"vscode-languageserver-types": "^3.0.3"
"vscode-languageserver-types": "^3.0.3",
"image-size": "^0.5.2"
}
}
5 changes: 5 additions & 0 deletions extensions/emmet/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { evaluateMathExpression } from './evaluateMathExpression';
import { incrementDecrement } from './incrementDecrement';
import { LANGUAGE_MODES, getMappingForIncludedLanguages } from './util';
import { updateExtensionsPath } from 'vscode-emmet-helper';
import { updateImageSize } from './updateImageSize';
import * as path from 'path';

export function activate(context: vscode.ExtensionContext) {
Expand Down Expand Up @@ -113,6 +114,10 @@ export function activate(context: vscode.ExtensionContext) {
return incrementDecrement(-10);
}));

context.subscriptions.push(vscode.commands.registerCommand('emmet.updateImageSize', () => {
return updateImageSize();
}));

let currentExtensionsPath = undefined;
let resolveUpdateExtensionsPath = () => {
let extensionsPath = vscode.workspace.getConfiguration('emmet')['extensionsPath'];
Expand Down
119 changes: 119 additions & 0 deletions extensions/emmet/src/imageSizeHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

// Based on @sergeche's work on the emmet plugin for atom
// TODO: Move to https://github.com/emmetio/image-size

'use strict';

import * as path from 'path';
import * as http from 'http';
import * as https from 'https';
import { parse as parseUrl } from 'url';
import * as sizeOf from 'image-size';

const reUrl = /^https?:/;

/**
* Get size of given image file. Supports files from local filesystem,
* as well as URLs
* @param {String} file Path to local file or URL
* @return {Promise}
*/
export function getImageSize(file) {
file = file.replace(/^file:\/\//, '');
return reUrl.test(file) ? getImageSizeFromURL(file) : getImageSizeFromFile(file);
}

/**
* Get image size from file on local file system
* @param {String} file
* @return {Promise}
*/
function getImageSizeFromFile(file) {
return new Promise((resolve, reject) => {
const isDataUrl = file.match(/^data:.+?;base64,/);

if (isDataUrl) {
// NB should use sync version of `sizeOf()` for buffers
try {
const data = Buffer.from(file.slice(isDataUrl[0].length), 'base64');
return resolve(sizeForFileName('', sizeOf(data)));
} catch (err) {
return reject(err);
}
}

sizeOf(file, (err, size) => {
if (err) {
reject(err);
} else {
resolve(sizeForFileName(path.basename(file), size));
}
});
});
}

/**
* Get image size from given remove URL
* @param {String} url
* @return {Promise}
*/
function getImageSizeFromURL(url) {
return new Promise((resolve, reject) => {
url = parseUrl(url);
const getTransport = url.protocol === 'https:' ? https.get : http.get;

getTransport(url, resp => {
const chunks = [];
let bufSize = 0;

const trySize = chunks => {
try {
const size = sizeOf(Buffer.concat(chunks, bufSize));
resp.removeListener('data', onData);
resp.destroy(); // no need to read further
resolve(sizeForFileName(path.basename(url.pathname), size));
} catch (err) {
// might not have enough data, skip error
}
};

const onData = chunk => {
bufSize += chunk.length;
chunks.push(chunk);
trySize(chunks);
};

resp
.on('data', onData)
.on('end', () => trySize(chunks))
.once('error', err => {
resp.removeListener('data', onData);
reject(err);
});
})
.once('error', reject);
});
}

/**
* Returns size object for given file name. If file name contains `@Nx` token,
* the final dimentions will be downscaled by N
* @param {String} fileName
* @param {Object} size
* @return {Object}
*/
function sizeForFileName(fileName, size) {
const m = fileName.match(/@(\d+)x\./);
const scale = m ? +m[1] : 1;

return {
realWidth: size.width,
realHeight: size.height,
width: Math.floor(size.width / scale),
height: Math.floor(size.height / scale)
};
}
91 changes: 91 additions & 0 deletions extensions/emmet/src/locateFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

// Based on @sergeche's work on the emmet plugin for atom
// TODO: Move to https://github.com/emmetio/file-utils
'use strict';

import * as path from 'path';
import * as fs from 'fs';

const reAbsolute = /^\/+/;

/**
* Locates given `filePath` on user’s file system and returns absolute path to it.
* This method expects either URL, or relative/absolute path to resource
* @param {String} basePath Base path to use if filePath is not absoulte
* @param {String} filePath File to locate.
* @return {Promise}
*/
export function locateFile(base: string, filePath: string): Promise<string> {
if (/^\w+:/.test(filePath)) {
// path with protocol, already absolute
return Promise.resolve(filePath);
}

filePath = path.normalize(filePath);

return reAbsolute.test(filePath)
? resolveAbsolute(base, filePath)
: resolveRelative(base, filePath);
}

/**
* Resolves relative file path
* @param {TextEditor|String} base
* @param {String} filePath
* @return {Promise}
*/
function resolveRelative(basePath, filePath): Promise<string> {
return tryFile(path.resolve(basePath, filePath));
}

/**
* Resolves absolute file path agaist given editor: tries to find file in every
* parent of editor’s file
* @param {TextEditor|String} base
* @param {String} filePath
* @return {Promise}
*/
function resolveAbsolute(basePath, filePath): Promise<string> {
return new Promise((resolve, reject) => {
filePath = filePath.replace(reAbsolute, '');

const next = ctx => {
tryFile(path.resolve(ctx, filePath))
.then(resolve, err => {
const dir = path.dirname(ctx);
if (!dir || dir === ctx) {
return reject(`Unable to locate absolute file ${filePath}`);
}

next(dir);
});
};

next(basePath);
});
}

/**
* Check if given file exists and it’s a file, not directory
* @param {String} file
* @return {Promise}
*/
function tryFile(file): Promise<string> {
return new Promise((resolve, reject) => {
fs.stat(file, (err, stat) => {
if (err) {
return reject(err);
}

if (!stat.isFile()) {
return reject(new Error(`${file} is not a file`));
}

resolve(file);
});
});
}
15 changes: 15 additions & 0 deletions extensions/emmet/src/typings/EmmetNode.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ declare module 'EmmetNode' {
toString(): string
}

export interface CssToken extends Token {
size: number
item(number): any
type: string
}

export interface HtmlToken extends Token {
value: string
}

export interface Attribute extends Token {
name: string
value: Token
Expand Down Expand Up @@ -51,10 +61,15 @@ declare module 'EmmetNode' {

export interface Rule extends CssNode {
selectorToken: Token
contentStartToken: Token
contentEndToken: Token
}

export interface Property extends CssNode {
valueToken: Token
separator: Token
parent: Rule
terminatorToken: Token
}

export interface Stylesheet extends Node {
Expand Down
Loading

0 comments on commit 0bee43b

Please sign in to comment.