Skip to content

Commit

Permalink
Fix #46639
Browse files Browse the repository at this point in the history
  • Loading branch information
octref committed Apr 11, 2018
1 parent c17fb0e commit 263ab1f
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 24 deletions.
4 changes: 2 additions & 2 deletions extensions/css-language-features/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/client/out/**/*.js"]
// "preLaunchTask": "npm"
"outFiles": ["${workspaceFolder}/client/out/**/*.js"],
"preLaunchTask": "npm"
},
{
"name": "Launch Tests",
Expand Down
4 changes: 2 additions & 2 deletions extensions/css-language-features/server/src/cssServerMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
let capabilities: ServerCapabilities & FoldingProviderServerCapabilities = {
// Tell the client that the server works in FULL text document sync mode
textDocumentSync: documents.syncKind,
completionProvider: snippetSupport ? { resolveProvider: false } : undefined,
completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/'] } : undefined,
hoverProvider: true,
documentSymbolProvider: true,
referencesProvider: true,
Expand Down Expand Up @@ -192,7 +192,7 @@ connection.onCompletion((textDocumentPosition, token) => {
cssLS.setCompletionParticipants([getPathCompletionParticipant(document, workspaceFolders, pathCompletionList)]);
const result = cssLS.doComplete(document, textDocumentPosition.position, stylesheets.get(document))!; /* TODO: remove ! once LS has null annotations */
return {
isIncomplete: result.isIncomplete,
isIncomplete: pathCompletionList.isIncomplete,
items: [...pathCompletionList.items, ...result.items]
};
}, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token);
Expand Down
48 changes: 28 additions & 20 deletions extensions/css-language-features/server/src/pathCompletion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,29 @@ export function getPathCompletionParticipant(
): ICompletionParticipant {
return {
onURILiteralValue: ({ position, range, uriValue }) => {
const isValueQuoted = startsWith(uriValue, `'`) || startsWith(uriValue, `"`);
const fullValue = stripQuotes(uriValue);
const valueBeforeCursor = isValueQuoted
? fullValue.slice(0, position.character - (range.start.character + 1))
: fullValue.slice(0, position.character - range.start.character);

if (shouldDoPathCompletion(fullValue)) {
if (!workspaceFolders || workspaceFolders.length === 0) {
return;
}
const workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders);
if (fullValue === '.' || fullValue === '..') {
result.isIncomplete = true;
return;
}

const paths = providePaths(fullValue, URI.parse(document.uri).fsPath, workspaceRoot);
result.items = [...paths.map(p => pathToSuggestion(p, fullValue, fullValue, range)), ...result.items];
if (!workspaceFolders || workspaceFolders.length === 0) {
return;
}
const workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders);
const paths = providePaths(valueBeforeCursor, URI.parse(document.uri).fsPath, workspaceRoot);

const fullValueRange = isValueQuoted ? shiftRange(range, 1, -1) : range;
const replaceRange = pathToReplaceRange(valueBeforeCursor, fullValue, fullValueRange);
const suggestions = paths.map(p => pathToSuggestion(p, replaceRange));
result.items = [...suggestions, ...result.items];
}

};
}

Expand All @@ -44,13 +55,6 @@ function stripQuotes(fullValue: string) {
}
}

function shouldDoPathCompletion(fullValue: string) {
if (fullValue === '.') {
return false;
}
return true;
}

/**
* Get a list of path suggestions. Folder suggestions are suffixed with a slash.
*/
Expand Down Expand Up @@ -85,29 +89,33 @@ const isDir = (p: string) => {
}
};

function pathToSuggestion(p: string, valueBeforeCursor: string, fullValue: string, range: Range): CompletionItem {
const isDir = p[p.length - 1] === '/';

function pathToReplaceRange(valueBeforeCursor: string, fullValue: string, fullValueRange: Range) {
let replaceRange: Range;
const lastIndexOfSlash = valueBeforeCursor.lastIndexOf('/');
if (lastIndexOfSlash === -1) {
replaceRange = shiftRange(range, 1, -1);
replaceRange = fullValueRange;
} else {
// For cases where cursor is in the middle of attribute value, like <script src="./s|rc/test.js">
// Find the last slash before cursor, and calculate the start of replace range from there
const valueAfterLastSlash = fullValue.slice(lastIndexOfSlash + 1);
const startPos = shiftPosition(range.end, -1 - valueAfterLastSlash.length);
const startPos = shiftPosition(fullValueRange.end, -valueAfterLastSlash.length);
// If whitespace exists, replace until it
const whiteSpaceIndex = valueAfterLastSlash.indexOf(' ');
let endPos;
if (whiteSpaceIndex !== -1) {
endPos = shiftPosition(startPos, whiteSpaceIndex);
} else {
endPos = shiftPosition(range.end, -1);
endPos = fullValueRange.end;
}
replaceRange = Range.create(startPos, endPos);
}

return replaceRange;
}

function pathToSuggestion(p: string, replaceRange: Range): CompletionItem {
const isDir = p[p.length - 1] === '/';

if (isDir) {
return {
label: p,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,34 @@ suite('Completions', () => {
]
}, testUri, folders);
});

test('CSS Path Completion - Unquoted url', function () {
let testUri = Uri.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString();
let folders = [{ name: 'x', uri: Uri.file(path.resolve(__dirname, '../../test')).toString() }];

assertCompletions('html { background-image: url(./|)', {
items: [
{ label: 'about.html', resultText: 'html { background-image: url(./about.html)' }
]
}, testUri, folders);

assertCompletions('html { background-image: url(./a|)', {
items: [
{ label: 'about.html', resultText: 'html { background-image: url(./about.html)' }
]
}, testUri, folders);

assertCompletions('html { background-image: url(../|src/)', {
items: [
{ label: 'about/', resultText: 'html { background-image: url(../about/)' }
]
}, testUri, folders);

assertCompletions('html { background-image: url(../s|rc/)', {
items: [
{ label: 'about/', resultText: 'html { background-image: url(../about/)' }
]
}, testUri, folders);
});

});

0 comments on commit 263ab1f

Please sign in to comment.