From 9a0b59c55bc7199b5d79d94f5d391aba4d8c7e38 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 21 Feb 2018 07:58:38 -0800 Subject: [PATCH] basic completion --- extensions/html/server/src/htmlServerMain.ts | 14 +++- extensions/html/server/src/modes/htmlMode.ts | 11 ++++ .../html/server/src/modes/pathCompletion.ts | 65 +++++++++++++++++++ 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 extensions/html/server/src/modes/pathCompletion.ts diff --git a/extensions/html/server/src/htmlServerMain.ts b/extensions/html/server/src/htmlServerMain.ts index 8d04a8a8590..e755a4fd0a3 100644 --- a/extensions/html/server/src/htmlServerMain.ts +++ b/extensions/html/server/src/htmlServerMain.ts @@ -18,6 +18,7 @@ import { getDocumentContext } from './utils/documentContext'; import uri from 'vscode-uri'; import { formatError, runSafe } from './utils/errors'; import { doComplete as emmetDoComplete, updateExtensionsPath as updateEmmetExtensionsPath, getEmmetCompletionParticipants } from 'vscode-emmet-helper'; +import { getPathCompletionParticipant } from './modes/pathCompletion'; namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); @@ -272,9 +273,16 @@ connection.onCompletion(async textDocumentPosition => { isIncomplete: true, items: undefined }; + let pathCompletionList: CompletionList = { + isIncomplete: false, + items: undefined + }; + if (mode.setCompletionParticipants) { const emmetCompletionParticipant = getEmmetCompletionParticipants(document, textDocumentPosition.position, mode.getId(), emmetSettings, emmetCompletionList); - mode.setCompletionParticipants([emmetCompletionParticipant]); + const pathCompletionParticipant = getPathCompletionParticipant(document, textDocumentPosition.position, pathCompletionList, workspaceFolders); + + mode.setCompletionParticipants([emmetCompletionParticipant, pathCompletionParticipant]); } let settings = await getDocumentSettings(document, () => mode.doComplete.length > 2); @@ -284,9 +292,9 @@ connection.onCompletion(async textDocumentPosition => { if (emmetCompletionList.items.length && hexColorRegex.test(emmetCompletionList.items[0].label) && result.items.some(x => x.label === emmetCompletionList.items[0].label)) { emmetCompletionList.items.shift(); } - return { isIncomplete: true, items: [...emmetCompletionList.items, ...result.items] }; + return { isIncomplete: true, items: [...emmetCompletionList.items, ...pathCompletionList.items, ...result.items] }; } - return result; + return { isIncomplete: false, items: [...pathCompletionList.items, ...result.items] }; }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`); }); diff --git a/extensions/html/server/src/modes/htmlMode.ts b/extensions/html/server/src/modes/htmlMode.ts index 24dcd14d49c..b746982736d 100644 --- a/extensions/html/server/src/modes/htmlMode.ts +++ b/extensions/html/server/src/modes/htmlMode.ts @@ -32,10 +32,21 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService): LanguageM const node = htmlDocument.findNodeBefore(offset); const scanner = htmlLanguageService.createScanner(document.getText(), node.start); let token = scanner.scan(); + let prevTag, prevAttributeName; while (token !== TokenType.EOS && scanner.getTokenOffset() <= offset) { if (token === TokenType.Content && offset <= scanner.getTokenEnd()) { completionParticipants.forEach(participant => { if (participant.onHtmlContent) { participant.onHtmlContent(); } }); break; + } else if (token === TokenType.AttributeName) { + prevAttributeName = scanner.getTokenText(); + } else if (token === TokenType.StartTag) { + prevTag = scanner.getTokenText(); + } else if (token === TokenType.AttributeValue && offset <= scanner.getTokenEnd()) { + completionParticipants.forEach(participant => { + if (participant.onHtmlAttributeValue) { + participant.onHtmlAttributeValue(prevTag, prevAttributeName, scanner.getTokenText()); + } + }); } token = scanner.scan(); } diff --git a/extensions/html/server/src/modes/pathCompletion.ts b/extensions/html/server/src/modes/pathCompletion.ts new file mode 100644 index 00000000000..29eaa36c0ea --- /dev/null +++ b/extensions/html/server/src/modes/pathCompletion.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TextDocument, Position, CompletionList, CompletionItemKind } from 'vscode-languageserver-types'; +import { WorkspaceFolder } from 'vscode-languageserver-protocol/lib/protocol.workspaceFolders.proposed'; +import * as path from 'path'; +import * as fs from 'fs'; +import uri from 'vscode-uri'; + +export function getPathCompletionParticipant( + document: TextDocument, + position: Position, + result: CompletionList, + workspaceFolders: WorkspaceFolder[] | undefined +) { + return { + onHtmlAttributeValue: (tag, attributeName, attributeValue) => { + const pathTagAndAttribute: { [t: string]: string } = { + a: 'href', + script: 'src', + img: 'src', + link: 'href' + }; + + const isDir = (p: string) => fs.statSync(p).isDirectory(); + + if (pathTagAndAttribute[tag] && pathTagAndAttribute[tag] === attributeName) { + const currPath = attributeValue.replace(/['"]/g, ''); + + let resolvedPath; + if (currPath.startsWith('/')) { + if (!workspaceFolders || workspaceFolders.length === 0) { + return; + } + for (let i = 0; i < workspaceFolders.length; i++) { + if (document.uri.indexOf(workspaceFolders[i].uri) !== -1) { + resolvedPath = path.resolve(uri.parse(workspaceFolders[i].uri).fsPath); + } + } + } else { + resolvedPath = path.resolve(uri.parse(document.uri).fsPath, '..', currPath); + } + + if (resolvedPath && isDir(resolvedPath)) { + const filesAndFolders = fs.readdirSync(resolvedPath); + if (!result.items) { + result.items = []; + } + for (let i = 0; i < filesAndFolders.length; i++) { + const kind = isDir(path.resolve(resolvedPath, filesAndFolders[i])) + ? CompletionItemKind.Folder + : CompletionItemKind.File; + result.items.push({ + label: filesAndFolders[i], + kind + }); + } + } + } + } + }; +} -- GitLab