-
Notifications
You must be signed in to change notification settings - Fork 30.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add drop/paste resource for css (#221612)
* New drop and paste providers that create a url function snippet * added url pasting feature * added url pasting feature * added url pasting feature * Target just dropping/pasting resources for now * Move files * Remove unused strings * Removing more unused logic for now * Remove tsconfig change * Remove doc file * Capitalize * Remove old proposal names --------- Co-authored-by: Meghan Kulkarni <[email protected]> Co-authored-by: Martin Aeschlimann <[email protected]>
- Loading branch information
1 parent
06f8ef1
commit 8d40a80
Showing
7 changed files
with
248 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
153 changes: 153 additions & 0 deletions
153
extensions/css-language-features/client/src/dropOrPaste/dropOrPasteResource.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import * as path from 'path'; | ||
import * as vscode from 'vscode'; | ||
import { getDocumentDir, Mimes, Schemes } from './shared'; | ||
import { UriList } from './uriList'; | ||
|
||
class DropOrPasteResourceProvider implements vscode.DocumentDropEditProvider, vscode.DocumentPasteEditProvider { | ||
readonly kind = vscode.DocumentDropOrPasteEditKind.Empty.append('css', 'url'); | ||
|
||
async provideDocumentDropEdits( | ||
document: vscode.TextDocument, | ||
position: vscode.Position, | ||
dataTransfer: vscode.DataTransfer, | ||
token: vscode.CancellationToken, | ||
): Promise<vscode.DocumentDropEdit | undefined> { | ||
const uriList = await this.getUriList(dataTransfer); | ||
if (!uriList.entries.length || token.isCancellationRequested) { | ||
return; | ||
} | ||
|
||
const snippet = await this.createUriListSnippet(uriList); | ||
if (!snippet || token.isCancellationRequested) { | ||
return; | ||
} | ||
|
||
return { | ||
kind: this.kind, | ||
title: snippet.label, | ||
insertText: snippet.snippet.value, | ||
yieldTo: this.pasteAsCssUrlByDefault(document, position) ? [] : [vscode.DocumentDropOrPasteEditKind.Empty.append('uri')] | ||
}; | ||
} | ||
|
||
async provideDocumentPasteEdits( | ||
document: vscode.TextDocument, | ||
ranges: readonly vscode.Range[], | ||
dataTransfer: vscode.DataTransfer, | ||
_context: vscode.DocumentPasteEditContext, | ||
token: vscode.CancellationToken | ||
): Promise<vscode.DocumentPasteEdit[] | undefined> { | ||
const uriList = await this.getUriList(dataTransfer); | ||
if (!uriList.entries.length || token.isCancellationRequested) { | ||
return; | ||
} | ||
|
||
const snippet = await this.createUriListSnippet(uriList); | ||
if (!snippet || token.isCancellationRequested) { | ||
return; | ||
} | ||
|
||
return [{ | ||
kind: this.kind, | ||
title: snippet.label, | ||
insertText: snippet.snippet.value, | ||
yieldTo: this.pasteAsCssUrlByDefault(document, ranges[0].start) ? [] : [vscode.DocumentDropOrPasteEditKind.Empty.append('uri')] | ||
}]; | ||
} | ||
|
||
private async getUriList(dataTransfer: vscode.DataTransfer): Promise<UriList> { | ||
const urlList = await dataTransfer.get(Mimes.uriList)?.asString(); | ||
if (urlList) { | ||
return UriList.from(urlList); | ||
} | ||
|
||
// Find file entries | ||
const uris: vscode.Uri[] = []; | ||
for (const [_, entry] of dataTransfer) { | ||
const file = entry.asFile(); | ||
if (file?.uri) { | ||
uris.push(file.uri); | ||
} | ||
} | ||
|
||
return new UriList(uris.map(uri => ({ uri, str: uri.toString(true) }))); | ||
} | ||
|
||
private async createUriListSnippet(uriList: UriList): Promise<{ readonly snippet: vscode.SnippetString; readonly label: string } | undefined> { | ||
if (!uriList.entries.length) { | ||
return; | ||
} | ||
|
||
const snippet = new vscode.SnippetString(); | ||
for (let i = 0; i < uriList.entries.length; i++) { | ||
const uri = uriList.entries[i]; | ||
const relativePath = getRelativePath(uri.uri); | ||
const urlText = relativePath ?? uri.str; | ||
|
||
snippet.appendText(`url(${urlText})`); | ||
if (i !== uriList.entries.length - 1) { | ||
snippet.appendText(' '); | ||
} | ||
} | ||
|
||
return { | ||
snippet, | ||
label: uriList.entries.length > 1 | ||
? vscode.l10n.t('Insert url() Functions') | ||
: vscode.l10n.t('Insert url() Function') | ||
}; | ||
} | ||
|
||
private pasteAsCssUrlByDefault(document: vscode.TextDocument, position: vscode.Position): boolean { | ||
const regex = /url\(.+?\)/gi; | ||
for (const match of Array.from(document.lineAt(position.line).text.matchAll(regex))) { | ||
if (position.character > match.index && position.character < match.index + match[0].length) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
} | ||
|
||
function getRelativePath(file: vscode.Uri): string | undefined { | ||
const dir = getDocumentDir(file); | ||
if (dir && dir.scheme === file.scheme && dir.authority === file.authority) { | ||
if (file.scheme === Schemes.file) { | ||
// On windows, we must use the native `path.relative` to generate the relative path | ||
// so that drive-letters are resolved cast insensitively. However we then want to | ||
// convert back to a posix path to insert in to the document | ||
const relativePath = path.relative(dir.fsPath, file.fsPath); | ||
return path.posix.normalize(relativePath.split(path.sep).join(path.posix.sep)); | ||
} | ||
|
||
return path.posix.relative(dir.path, file.path); | ||
} | ||
|
||
return undefined; | ||
} | ||
|
||
export function registerDropOrPasteResourceSupport(selector: vscode.DocumentSelector): vscode.Disposable { | ||
const provider = new DropOrPasteResourceProvider(); | ||
|
||
return vscode.Disposable.from( | ||
vscode.languages.registerDocumentDropEditProvider(selector, provider, { | ||
providedDropEditKinds: [provider.kind], | ||
dropMimeTypes: [ | ||
Mimes.uriList, | ||
'files' | ||
] | ||
}), | ||
vscode.languages.registerDocumentPasteEditProvider(selector, provider, { | ||
providedPasteEditKinds: [provider.kind], | ||
pasteMimeTypes: [ | ||
Mimes.uriList, | ||
'files' | ||
] | ||
}) | ||
); | ||
} |
42 changes: 42 additions & 0 deletions
42
extensions/css-language-features/client/src/dropOrPaste/shared.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import * as vscode from 'vscode'; | ||
import { Utils } from 'vscode-uri'; | ||
|
||
export const Schemes = Object.freeze({ | ||
file: 'file', | ||
notebookCell: 'vscode-notebook-cell', | ||
untitled: 'untitled', | ||
}); | ||
|
||
export const Mimes = Object.freeze({ | ||
plain: 'text/plain', | ||
uriList: 'text/uri-list', | ||
}); | ||
|
||
|
||
export function getDocumentDir(uri: vscode.Uri): vscode.Uri | undefined { | ||
const docUri = getParentDocumentUri(uri); | ||
if (docUri.scheme === Schemes.untitled) { | ||
return vscode.workspace.workspaceFolders?.[0]?.uri; | ||
} | ||
return Utils.dirname(docUri); | ||
} | ||
|
||
function getParentDocumentUri(uri: vscode.Uri): vscode.Uri { | ||
if (uri.scheme === Schemes.notebookCell) { | ||
// is notebook documents necessary? | ||
for (const notebook of vscode.workspace.notebookDocuments) { | ||
for (const cell of notebook.getCells()) { | ||
if (cell.document.uri.toString() === uri.toString()) { | ||
return notebook.uri; | ||
} | ||
} | ||
} | ||
} | ||
|
||
return uri; | ||
} |
38 changes: 38 additions & 0 deletions
38
extensions/css-language-features/client/src/dropOrPaste/uriList.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import * as vscode from 'vscode'; | ||
|
||
function splitUriList(str: string): string[] { | ||
return str.split('\r\n'); | ||
} | ||
|
||
function parseUriList(str: string): string[] { | ||
return splitUriList(str) | ||
.filter(value => !value.startsWith('#')) // Remove comments | ||
.map(value => value.trim()); | ||
} | ||
|
||
export class UriList { | ||
|
||
static from(str: string): UriList { | ||
return new UriList(coalesce(parseUriList(str).map(line => { | ||
try { | ||
return { uri: vscode.Uri.parse(line), str: line }; | ||
} catch { | ||
// Uri parse failure | ||
return undefined; | ||
} | ||
}))); | ||
} | ||
|
||
constructor( | ||
public readonly entries: ReadonlyArray<{ readonly uri: vscode.Uri; readonly str: string }> | ||
) { } | ||
} | ||
|
||
function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] { | ||
return <T[]>array.filter(e => !!e); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters