Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: add vue file basic support, alias path, fix #79 #53 #101

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"Programming Languages"
],
"activationEvents": [
"onLanguage:scss"
"onLanguage:scss",
"onLanguage:vue"
],
"main": "./out/client.js",
"contributes": {
Expand Down Expand Up @@ -81,6 +82,13 @@
"default": true,
"description": "Allows prompt Functions."
},
"scss.aliasPaths": {
"type": "object",
"default": {
"@/": "src/"
},
"description": "Allows parse alias paths. ex: { '@/': 'src/' }"
},
"scss.suggestFunctionsInStringContextAfterSymbols": {
"type": "string",
"default": " (+-*%",
Expand Down
2 changes: 1 addition & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function activate(context: vscode.ExtensionContext) {
};

const clientOptions: LanguageClientOptions = {
documentSelector: ['scss'],
documentSelector: ['scss', 'vue'],
synchronize: {
configurationSection: ['scss'],
fileEvents: vscode.workspace.createFileSystemWatcher('**/*.scss')
Expand Down
31 changes: 29 additions & 2 deletions src/providers/completion.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
'use strict';

import { CompletionList, CompletionItemKind, TextDocument, Files, CompletionItem } from 'vscode-languageserver';
import {
CompletionList,
CompletionItemKind,
TextDocument,
Files,
CompletionItem,
Range,
TextEdit
} from 'vscode-languageserver';

import { IMixin, ISymbols } from '../types/symbols';
import { ISettings } from '../types/settings';
Expand Down Expand Up @@ -221,6 +229,20 @@ function createFunctionCompletionItems(
return completions;
}

function convertRange(document: TextDocument, offset: number, text: string) {
const start =
offset -
document
.getText()
.slice(0, offset)
.split('')
.reverse()
.findIndex(el => el === ' ' || el === ':');
const startPosition = document.positionAt(start);
const endPosition = document.positionAt(start + text.length);
return Range.create(startPosition, endPosition);
}

/**
* Do Completion :)
*/
Expand All @@ -237,7 +259,7 @@ export function doCompletion(
return null;
}

const resource = parseDocument(document, offset);
const resource = parseDocument(document, offset, settings);

storage.set(documentPath, resource.symbols);

Expand Down Expand Up @@ -268,5 +290,10 @@ export function doCompletion(
completions.items = completions.items.concat(functions);
}

completions.items = completions.items.map(el => ({
...el,
textEdit: TextEdit.replace(convertRange(document, offset, el.insertText ?? el.label), el.insertText ?? el.label)
}));

return completions;
}
10 changes: 8 additions & 2 deletions src/providers/goDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import StorageService from '../services/storage';
import { parseDocument } from '../services/parser';
import { getSymbolsRelatedToDocument } from '../utils/symbols';
import { getDocumentPath } from '../utils/document';
import { ISettings } from '../types/settings';

interface ISymbol {
document: string;
Expand Down Expand Up @@ -53,13 +54,18 @@ function getSymbols(symbolList: ISymbols[], identifier: IIdentifier, currentPath
/**
* Do Go Definition :)
*/
export function goDefinition(document: TextDocument, offset: number, storage: StorageService): Promise<Location> {
export function goDefinition(
document: TextDocument,
offset: number,
settings: ISettings,
storage: StorageService
): Promise<Location> {
const documentPath = Files.uriToFilePath(document.uri) || document.uri;
if (!documentPath) {
return Promise.resolve(null);
}

const resource = parseDocument(document, offset);
const resource = parseDocument(document, offset, settings);
const hoverNode = resource.node;
if (!hoverNode || !hoverNode.type) {
return Promise.resolve(null);
Expand Down
10 changes: 8 additions & 2 deletions src/providers/hover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { parseDocument } from '../services/parser';
import { getSymbolsCollection } from '../utils/symbols';
import { getDocumentPath } from '../utils/document';
import { getLimitedString } from '../utils/string';
import { ISettings } from '../types/settings';

/**
* Returns a colored (marked) line for Variable.
Expand Down Expand Up @@ -91,13 +92,18 @@ function getSymbol(symbolList: ISymbols[], identifier: any, currentPath: string)
/**
* Do Hover :)
*/
export function doHover(document: TextDocument, offset: number, storage: StorageService): Hover | null {
export function doHover(
document: TextDocument,
offset: number,
settings: ISettings,
storage: StorageService
): Hover | null {
const documentPath = Files.uriToFilePath(document.uri) || document.uri;
if (!documentPath) {
return null;
}

const resource = parseDocument(document, offset);
const resource = parseDocument(document, offset, settings);
const hoverNode = resource.node;
if (!hoverNode || !hoverNode.type) {
return null;
Expand Down
10 changes: 8 additions & 2 deletions src/providers/signatureHelp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { parseDocument } from '../services/parser';
import { getSymbolsCollection } from '../utils/symbols';
import { getTextBeforePosition } from '../utils/string';
import { hasInFacts } from '../utils/facts';
import { ISettings } from '../types/settings';

// RegExp's
const reNestedParenthesis = /\(([\w-]+)\(/;
Expand Down Expand Up @@ -140,7 +141,12 @@ function parseArgumentsAtLine(text: string): IMixinEntry {
/**
* Do Signature Help :)
*/
export function doSignatureHelp(document: TextDocument, offset: number, storage: StorageService): SignatureHelp {
export function doSignatureHelp(
document: TextDocument,
offset: number,
settings: ISettings,
storage: StorageService
): SignatureHelp {
const suggestions: { name: string; parameters: IVariable[] }[] = [];

const ret: SignatureHelp = {
Expand All @@ -167,7 +173,7 @@ export function doSignatureHelp(document: TextDocument, offset: number, storage:

const symbolType = textBeforeWord.indexOf('@include') !== -1 ? 'mixins' : 'functions';

const resource = parseDocument(document, offset);
const resource = parseDocument(document, offset, settings);

storage.set(documentPath, resource.symbols);

Expand Down
49 changes: 35 additions & 14 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { doSignatureHelp } from './providers/signatureHelp';
import { goDefinition } from './providers/goDefinition';
import { searchWorkspaceSymbol } from './providers/workspaceSymbol';
import { findFiles } from './utils/fs';
import { getSCSSRegionsDocument } from './utils/vue';

let workspaceRoot: string;
let settings: ISettings;
Expand All @@ -47,11 +48,11 @@ documents.listen(connection);
connection.onInitialize(
async (params: InitializeParams): Promise<InitializeResult> => {
workspaceRoot = params.rootPath;
settings = params.initializationOptions.settings;
settings = { ...params.initializationOptions.settings, workspaceRoot };
storageService = new StorageService();
scannerService = new ScannerService(storageService, settings);

const files = await findFiles('**/*.scss', {
const files = await findFiles('**/*.{scss,vue}', {
cwd: params.rootPath,
deep: settings.scannerDepth,
ignore: settings.scannerExclude
Expand Down Expand Up @@ -81,7 +82,7 @@ connection.onInitialize(
);

connection.onDidChangeConfiguration(params => {
settings = params.settings.scss;
settings = { ...params.settings.scss, workspaceRoot };
});

connection.onDidChangeWatchedFiles(event => {
Expand All @@ -91,27 +92,47 @@ connection.onDidChangeWatchedFiles(event => {
});

connection.onCompletion(textDocumentPosition => {
const document = documents.get(textDocumentPosition.textDocument.uri);
const offset = document.offsetAt(textDocumentPosition.position);
const { document, offset } = getSCSSRegionsDocument(
documents.get(textDocumentPosition.textDocument.uri),
textDocumentPosition.position
);
if (!document) {
return null;
}
return doCompletion(document, offset, settings, storageService);
});

connection.onHover(textDocumentPosition => {
const document = documents.get(textDocumentPosition.textDocument.uri);
const offset = document.offsetAt(textDocumentPosition.position);
return doHover(document, offset, storageService);
const { document, offset } = getSCSSRegionsDocument(
documents.get(textDocumentPosition.textDocument.uri),
textDocumentPosition.position
);
if (!document) {
return null;
}
return doHover(document, offset, settings, storageService);
});

connection.onSignatureHelp(textDocumentPosition => {
const document = documents.get(textDocumentPosition.textDocument.uri);
const offset = document.offsetAt(textDocumentPosition.position);
return doSignatureHelp(document, offset, storageService);
const { document, offset } = getSCSSRegionsDocument(
documents.get(textDocumentPosition.textDocument.uri),
textDocumentPosition.position
);
if (!document) {
return null;
}
return doSignatureHelp(document, offset, settings, storageService);
});

connection.onDefinition(textDocumentPosition => {
const document = documents.get(textDocumentPosition.textDocument.uri);
const offset = document.offsetAt(textDocumentPosition.position);
return goDefinition(document, offset, storageService);
const { document, offset } = getSCSSRegionsDocument(
documents.get(textDocumentPosition.textDocument.uri),
textDocumentPosition.position
);
if (!document) {
return null;
}
return goDefinition(document, offset, settings, storageService);
});

connection.onWorkspaceSymbol(workspaceSymbolParams => {
Expand Down
37 changes: 26 additions & 11 deletions src/services/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { getSCSSLanguageService, SymbolKind, DocumentLink } from 'vscode-css-lan
import { INode, NodeType } from '../types/nodes';
import { IDocument, ISymbols, IVariable, IImport } from '../types/symbols';
import { getNodeAtOffset, getParentNodeByType } from '../utils/ast';
import { ISettings } from '../types/settings';

// RegExp's
const reReferenceCommentGlobal = /\/\/\s*<reference\s*path=["'](.*)['"]\s*\/?>/g;
Expand All @@ -24,14 +25,14 @@ ls.configure({
/**
* Returns all Symbols in a single document.
*/
export function parseDocument(document: TextDocument, offset: number = null): IDocument {
export function parseDocument(document: TextDocument, offset: number = null, setting: ISettings): IDocument {
const ast = ls.parseStylesheet(document) as INode;
const documentPath = Files.uriToFilePath(document.uri) || document.uri;

const symbols: ISymbols = {
document: documentPath,
filepath: documentPath,
...findDocumentSymbols(document, ast)
...findDocumentSymbols(document, ast, setting)
};

// Get `<reference *> comments from document
Expand All @@ -42,7 +43,7 @@ export function parseDocument(document: TextDocument, offset: number = null): ID
symbols.imports.push({
css: filepath.endsWith('.css'),
dynamic: reDynamicPath.test(filepath),
filepath: resolveReference(filepath, documentPath),
filepath: resolveReference(filepath, documentPath, setting),
reference: true
});
});
Expand All @@ -54,9 +55,9 @@ export function parseDocument(document: TextDocument, offset: number = null): ID
};
}

function findDocumentSymbols(document: TextDocument, ast: INode): ISymbols {
function findDocumentSymbols(document: TextDocument, ast: INode, setting: ISettings): ISymbols {
const symbols = ls.findDocumentSymbols(document, ast);
const links = findDocumentLinks(document, ast);
const links = findDocumentLinks(document, ast, setting);

const result: ISymbols = {
functions: [],
Expand Down Expand Up @@ -96,9 +97,9 @@ function findDocumentSymbols(document: TextDocument, ast: INode): ISymbols {
return result;
}

function findDocumentLinks(document: TextDocument, ast: INode): DocumentLink[] {
function findDocumentLinks(document: TextDocument, ast: INode, setting: ISettings): DocumentLink[] {
const links = ls.findDocumentLinks(document, ast, {
resolveReference: (ref, base = Files.uriToFilePath(document.uri)) => resolveReference(ref, base)
resolveReference: (ref, base = Files.uriToFilePath(document.uri)) => resolveReference(ref, base, setting)
});

return links.map(link => ({
Expand All @@ -107,16 +108,30 @@ function findDocumentLinks(document: TextDocument, ast: INode): DocumentLink[] {
}));
}

export function resolveReference(ref: string, base: string): string {
export function resolveReference(ref: string, base: string, setting: ISettings): string {
let result = '';
if (ref[0] === '~') {
ref = 'node_modules/' + ref.slice(1);
for (const alias of Object.keys(setting.aliasPaths)) {
const prefix = `~${alias}`;
if (ref.startsWith(prefix)) {
result = path.join(setting.workspaceRoot, setting.aliasPaths[alias], ref.slice(prefix.length));
break;
}
}
if (result === '') {
result = path.join(setting.workspaceRoot, 'node_modules', ref.slice(1));
}
}

if (result === '') {
result = path.join(path.dirname(base), ref);
}

if (!ref.endsWith('.scss') && !ref.endsWith('.css')) {
ref += '.scss';
result += '.scss';
}

return path.join(path.dirname(base), ref);
return result;
}

function getVariableValue(ast: INode, offset: number): string | null {
Expand Down
10 changes: 8 additions & 2 deletions src/services/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ISettings } from '../types/settings';
import { readFile, fileExists } from '../utils/fs';
import { parseDocument } from './parser';
import StorageService from './storage';
import { isVueFile, getVueSCSSContent } from '../utils/vue';

export default class ScannerService {
constructor(private readonly _storage: StorageService, private readonly _settings: ISettings) {}
Expand Down Expand Up @@ -37,8 +38,13 @@ export default class ScannerService {
}

const content = await this._readFile(filepath);
const document = TextDocument.create(originalFilepath, 'scss', 1, content);
const { symbols } = parseDocument(document, null);
const document = TextDocument.create(
originalFilepath,
'scss',
1,
isVueFile(filepath) ? getVueSCSSContent(content) : content
);
const { symbols } = parseDocument(document, null, this._settings);

this._storage.set(filepath, { ...symbols, filepath });

Expand Down
2 changes: 1 addition & 1 deletion src/test/e2e/suite/completion/completion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('SCSS Completion Test', () => {
});

it('Offers completions from tilde imports', async () => {
await testCompletion(docUri, position(11, 11), [{ label: '$tilde', detail: 'node_modules/foo/bar.scss' }]);
await testCompletion(docUri, position(11, 11), [{ label: '$tilde', detail: '../node_modules/foo/bar.scss' }]);
});

it('Offers completions from partial file', async () => {
Expand Down
Loading