From c22d449ac51fb815d16db70c66ca38b60bf2d802 Mon Sep 17 00:00:00 2001 From: Jojoshua Date: Sat, 11 Jun 2022 14:59:20 -0400 Subject: [PATCH 1/7] Initial --- .vscode/settings.json | 6 +- .../plugins/css/CSSClassDefinitionLocator.ts | 169 ++++++++++++++++++ .../plugins/typescript/TypeScriptPlugin.ts | 32 ++++ .../plugins/typescript/svelte-ast-utils.ts | 3 + 4 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index f865d2f2c..a88eab873 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,7 @@ { - "typescript.preferences.quoteStyle": "single" + "typescript.preferences.quoteStyle": "single", + "files.exclude": { + "**/**/dist": true, + "**/**/node_modules": true + } } diff --git a/packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts b/packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts new file mode 100644 index 000000000..1798c958e --- /dev/null +++ b/packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts @@ -0,0 +1,169 @@ +import { Position, Range } from 'vscode-languageserver'; +import { SvelteDocumentSnapshot, SvelteSnapshotFragment } from '../typescript/DocumentSnapshot'; +import { Document } from '../../lib/documents'; +import { SvelteNode } from '../typescript/svelte-ast-utils'; +export class CSSClassDefinitionLocator { + text: string; + initialNodeAt: SvelteNode; + constructor( + public tsDoc: SvelteDocumentSnapshot, + public position: Position, + public document: Document, + public mainFragement: SvelteSnapshotFragment + ) { + const testEndTagRange = Range.create( + Position.create(position.line, 0), + Position.create(position.line, position.character) + ); + this.text = document.getText(testEndTagRange); + this.initialNodeAt = this.tsDoc.svelteNodeAt(this.position) as SvelteNode; + } + + public GetCSSClassDefinition() { + if (this.IsStandardClassFormat()) { + return this.GetStandardFormatClassName(); + } + + if (this.IsDirectiveFormat() && this.initialNodeAt.name) { + return this.GetDefinitionRangeWithinStyleSection(`.${this.initialNodeAt.name}`); + } + + if (this.IsConditionalExpressionClassFormat() && this.initialNodeAt.value) { + return this.GetDefinitionRangeWithinStyleSection(`.${this.initialNodeAt.value}`); + } + + return false; + } + + /** + * Standard format: + * class="test test1" + */ + public IsStandardClassFormat() { + if (this.initialNodeAt?.type == 'Text' && this.initialNodeAt?.parent?.name == 'class') { + return true; + } + + return false; + } + + /** + * Conditional Expression format: + * class="{current === 'baz' ? 'selected' : '' + */ + public IsConditionalExpressionClassFormat() { + if ( + this.initialNodeAt?.type == 'Literal' && + this.initialNodeAt?.parent?.type == 'ConditionalExpression' && + this.initialNodeAt?.parent.parent?.parent?.name == 'class' + ) { + return true; + } + + return false; + } + + /** + * Class Directive format: + * class:active="{current === 'foo'}" + */ + public IsDirectiveFormat() { + if (this.initialNodeAt?.type == 'Class' && this.initialNodeAt?.parent?.type == 'Element') { + return true; + } + + return false; + } + + public GetStandardFormatClassName() { + let loopLength = this.text.length; + let testPosition = this.position.character; + let spaceCount = 0; + + //Go backwards until hitting a " and keep track of how many spaces happened along the way + while (loopLength) { + const testEndTagRange = Range.create( + Position.create(this.position.line, testPosition - 1), + Position.create(this.position.line, testPosition) + ); + const text = this.document.getText(testEndTagRange); + if (text === ' ') { + spaceCount++; + } + + if (text === '"') { + break; + } + + testPosition--; + loopLength--; + } + + const cssClassName = this.initialNodeAt?.data.split(' ')[spaceCount]; + + return this.GetDefinitionRangeWithinStyleSection(`.${cssClassName}`); + } + + public GetDefinitionRangeWithinStyleSection(targetClass: string) { + let indexOccurence = this.document.content.indexOf(targetClass, 0); + + while (indexOccurence >= 0) { + if (this.IsOffsetWithinStyleSection(indexOccurence)) { + const startPosition = this.document.positionAt(indexOccurence); + const targetRange = Range.create( + startPosition, + Position.create( + startPosition.line, + startPosition.character + targetClass.length + ) + ); + indexOccurence = this.document.content.indexOf(targetClass, indexOccurence + 1); + + if (!this.IsExactClassMatch(targetRange)) { + continue; + } + + return targetRange; + } + } + } + + public IsOffsetWithinStyleSection(offsetPosition: number) { + if (this.document.styleInfo) { + if ( + offsetPosition > this.document.styleInfo?.start && + offsetPosition < this.document.styleInfo?.end + ) { + return true; + } + } + + return false; + } + + public IsExactClassMatch(testRange: Range) { + //Check nothing before the test position + if (testRange.start.character > 0) { + const beforerange = Range.create( + Position.create(testRange.start.line, testRange.start.character - 1), + Position.create(testRange.start.line, testRange.start.character) + ); + this.text = this.document.getText(beforerange).trim(); + if (this.text) { + return false; + } + } + + //Check space or { is after the test position + const afterRange = Range.create( + Position.create(testRange.end.line, testRange.end.character), + Position.create(testRange.end.line, testRange.end.character + 1) + ); + this.text = this.document.getText(afterRange).trim(); + if (this.text == '' || this.text == '{') { + return true; + } + + return false; + } +} diff --git a/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts b/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts index e8ad66e52..5f670c793 100644 --- a/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts +++ b/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts @@ -26,6 +26,7 @@ import { import { Document, getTextInRange, mapSymbolInformationToOriginal } from '../../lib/documents'; import { LSConfigManager, LSTypescriptConfig } from '../../ls-config'; import { isNotNullOrUndefined, isZeroLengthRange, pathToUrl } from '../../utils'; +import { CSSClassDefinitionLocator } from '../css/CSSClassDefinitionLocator'; import { AppCompletionItem, AppCompletionList, @@ -329,6 +330,37 @@ export class TypeScriptPlugin const { lang, tsDoc } = await this.getLSAndTSDoc(document); const mainFragment = tsDoc.getFragment(); + const cssClassHelper = new CSSClassDefinitionLocator( + tsDoc, + position, + document, + mainFragment + ); + + const cssDefinitionRange = cssClassHelper.GetCSSClassDefinition(); + if (cssDefinitionRange) { + const results: DefinitionLink[] = []; + cssDefinitionRange.start.character++; //Report start of name instead of start at . for easy rename (F2) possibilities + + const originRange = Range.create( + Position.create(position.line, position.character), + Position.create(position.line, position.character) + ); + + results.push( + LocationLink.create( + pathToUrl(document.getFilePath() as string), + cssDefinitionRange, + cssDefinitionRange, + originRange + ) + ); + + if (results) { + return results; + } + } + const defs = lang.getDefinitionAndBoundSpan( tsDoc.filePath, mainFragment.offsetAt(mainFragment.getGeneratedPosition(position)) diff --git a/packages/language-server/src/plugins/typescript/svelte-ast-utils.ts b/packages/language-server/src/plugins/typescript/svelte-ast-utils.ts index b4c6c7466..4fc49af61 100644 --- a/packages/language-server/src/plugins/typescript/svelte-ast-utils.ts +++ b/packages/language-server/src/plugins/typescript/svelte-ast-utils.ts @@ -3,6 +3,9 @@ export interface SvelteNode { end: number; type: string; parent?: SvelteNode; + value?: any; + data?: any; + name?: string; } /** From 6e42c846a0b0f6f1607657d594a3d2d8d309fd2e Mon Sep 17 00:00:00 2001 From: Jojoshua Date: Sat, 11 Jun 2022 15:40:34 -0400 Subject: [PATCH 2/7] refactor --- .../plugins/css/CSSClassDefinitionLocator.ts | 27 +++++++++---------- .../plugins/typescript/TypeScriptPlugin.ts | 8 +----- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts b/packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts index 1798c958e..4ba09a633 100644 --- a/packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts +++ b/packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts @@ -1,21 +1,15 @@ import { Position, Range } from 'vscode-languageserver'; -import { SvelteDocumentSnapshot, SvelteSnapshotFragment } from '../typescript/DocumentSnapshot'; +import { SvelteDocumentSnapshot } from '../typescript/DocumentSnapshot'; import { Document } from '../../lib/documents'; import { SvelteNode } from '../typescript/svelte-ast-utils'; export class CSSClassDefinitionLocator { - text: string; + //text: string; initialNodeAt: SvelteNode; constructor( public tsDoc: SvelteDocumentSnapshot, public position: Position, - public document: Document, - public mainFragement: SvelteSnapshotFragment + public document: Document ) { - const testEndTagRange = Range.create( - Position.create(position.line, 0), - Position.create(position.line, position.character) - ); - this.text = document.getText(testEndTagRange); this.initialNodeAt = this.tsDoc.svelteNodeAt(this.position) as SvelteNode; } @@ -76,7 +70,13 @@ export class CSSClassDefinitionLocator { } public GetStandardFormatClassName() { - let loopLength = this.text.length; + const testEndTagRange = Range.create( + Position.create(this.position.line, 0), + Position.create(this.position.line, this.position.character) + ); + const text = this.document.getText(testEndTagRange); + + let loopLength = text.length; let testPosition = this.position.character; let spaceCount = 0; @@ -148,8 +148,7 @@ export class CSSClassDefinitionLocator { Position.create(testRange.start.line, testRange.start.character - 1), Position.create(testRange.start.line, testRange.start.character) ); - this.text = this.document.getText(beforerange).trim(); - if (this.text) { + if (this.document.getText(beforerange).trim()) { return false; } } @@ -159,8 +158,8 @@ export class CSSClassDefinitionLocator { Position.create(testRange.end.line, testRange.end.character), Position.create(testRange.end.line, testRange.end.character + 1) ); - this.text = this.document.getText(afterRange).trim(); - if (this.text == '' || this.text == '{') { + const afterRangeText = this.document.getText(afterRange).trim(); + if (afterRangeText == '' || afterRangeText == '{') { return true; } diff --git a/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts b/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts index 5f670c793..90abe9cb8 100644 --- a/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts +++ b/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts @@ -330,13 +330,7 @@ export class TypeScriptPlugin const { lang, tsDoc } = await this.getLSAndTSDoc(document); const mainFragment = tsDoc.getFragment(); - const cssClassHelper = new CSSClassDefinitionLocator( - tsDoc, - position, - document, - mainFragment - ); - + const cssClassHelper = new CSSClassDefinitionLocator(tsDoc, position, document); const cssDefinitionRange = cssClassHelper.GetCSSClassDefinition(); if (cssDefinitionRange) { const results: DefinitionLink[] = []; From 65920eaa831756aa44ebebf339637b0d673698ce Mon Sep 17 00:00:00 2001 From: Jojoshua Date: Sat, 11 Jun 2022 15:41:04 -0400 Subject: [PATCH 3/7] refactor --- .../language-server/src/plugins/css/CSSClassDefinitionLocator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts b/packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts index 4ba09a633..52c88e2fd 100644 --- a/packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts +++ b/packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts @@ -3,7 +3,6 @@ import { SvelteDocumentSnapshot } from '../typescript/DocumentSnapshot'; import { Document } from '../../lib/documents'; import { SvelteNode } from '../typescript/svelte-ast-utils'; export class CSSClassDefinitionLocator { - //text: string; initialNodeAt: SvelteNode; constructor( public tsDoc: SvelteDocumentSnapshot, From bd2b9c449f01e4c370661ef9c1889f0ac6e2fb46 Mon Sep 17 00:00:00 2001 From: Jojoshua Date: Sat, 11 Jun 2022 16:30:23 -0400 Subject: [PATCH 4/7] Add tests --- packages/language-server/package.json | 1 + .../typescript/TypescriptPlugin.test.ts | 126 ++++++++++++++++++ .../testfiles/css-definitions.svelte | 55 ++++++++ 3 files changed, 182 insertions(+) create mode 100644 packages/language-server/test/plugins/typescript/testfiles/css-definitions.svelte diff --git a/packages/language-server/package.json b/packages/language-server/package.json index 0ec222701..c52dedde4 100644 --- a/packages/language-server/package.json +++ b/packages/language-server/package.json @@ -6,6 +6,7 @@ "typings": "dist/src/index", "scripts": { "test": "cross-env TS_NODE_TRANSPILE_ONLY=true mocha --require ts-node/register \"test/**/*.ts\" --exclude \"test/**/*.d.ts\"", + "TypescriptPlugin": "cross-env TS_NODE_TRANSPILE_ONLY=true mocha --require ts-node/register \"test/**/TypescriptPlugin.test.ts\" --exclude \"test/**/*.d.ts\"", "build": "tsc", "prepublishOnly": "npm run build", "watch": "tsc -w" diff --git a/packages/language-server/test/plugins/typescript/TypescriptPlugin.test.ts b/packages/language-server/test/plugins/typescript/TypescriptPlugin.test.ts index 91d3e3409..dfcf7d5de 100644 --- a/packages/language-server/test/plugins/typescript/TypescriptPlugin.test.ts +++ b/packages/language-server/test/plugins/typescript/TypescriptPlugin.test.ts @@ -791,6 +791,132 @@ function test(useNewTransformation: boolean) { ]); } + it('provides standard css definition from svelte template', async () => { + const { plugin, document } = setup('css-definitions.svelte'); + + const definitions = await plugin.getDefinitions(document, Position.create(12, 19)); + + assert.deepStrictEqual(definitions, [ + { + targetRange: { + start: { + line: 22, + character: 3 + }, + end: { + line: 22, + character: 8 + } + }, + targetSelectionRange: { + start: { + line: 22, + character: 3 + }, + end: { + line: 22, + character: 8 + } + }, + originSelectionRange: { + start: { + line: 12, + character: 19 + }, + end: { + line: 12, + character: 19 + } + }, + targetUri: getUri('css-definitions.svelte') + } + ]); + }); + + it('provides conditional expression css definition from svelte template', async () => { + const { plugin, document } = setup('css-definitions.svelte'); + + const definitions = await plugin.getDefinitions(document, Position.create(16, 33)); + + assert.deepStrictEqual(definitions, [ + { + targetRange: { + start: { + line: 50, + character: 3 + }, + end: { + line: 50, + character: 11 + } + }, + targetSelectionRange: { + start: { + line: 50, + character: 3 + }, + end: { + line: 50, + character: 11 + } + }, + originSelectionRange: { + start: { + line: 16, + character: 33 + }, + end: { + line: 16, + character: 33 + } + }, + targetUri: getUri('css-definitions.svelte') + } + ]); + }); + + it('provides class directive css definition from svelte template', async () => { + const { plugin, document } = setup('css-definitions.svelte'); + + const definitions = await plugin.getDefinitions(document, Position.create(12, 31)); + + assert.deepStrictEqual(definitions, [ + { + targetRange: { + start: { + line: 45, + character: 3 + }, + end: { + line: 45, + character: 9 + } + }, + targetSelectionRange: { + start: { + line: 45, + character: 3 + }, + end: { + line: 45, + character: 9 + } + }, + originSelectionRange: { + start: { + line: 12, + character: 31 + }, + end: { + line: 12, + character: 31 + } + }, + targetUri: getUri('css-definitions.svelte') + } + ]); + }); + it('(within script simple)', async () => { await test$StoreDef( Position.create(9, 1), diff --git a/packages/language-server/test/plugins/typescript/testfiles/css-definitions.svelte b/packages/language-server/test/plugins/typescript/testfiles/css-definitions.svelte new file mode 100644 index 000000000..3e09fbbc9 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/css-definitions.svelte @@ -0,0 +1,55 @@ + + +
+

Visit the Svelte tutorial to learn how to build Svelte apps.

+
+ +
+

Visit the Svelte tutorial to learn how to build Svelte apps.

+
+ + From b01e66c67c69667166ce4ec545ed59d5fbf9289b Mon Sep 17 00:00:00 2001 From: Jojoshua Date: Tue, 14 Jun 2022 19:03:46 -0400 Subject: [PATCH 5/7] cleanup --- .../CSSClassDefinitionLocator.ts | 38 +++++++++---------- .../plugins/typescript/TypeScriptPlugin.ts | 4 +- 2 files changed, 21 insertions(+), 21 deletions(-) rename packages/language-server/src/plugins/{css => typescript}/CSSClassDefinitionLocator.ts (80%) diff --git a/packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts b/packages/language-server/src/plugins/typescript/CSSClassDefinitionLocator.ts similarity index 80% rename from packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts rename to packages/language-server/src/plugins/typescript/CSSClassDefinitionLocator.ts index 52c88e2fd..88aceebce 100644 --- a/packages/language-server/src/plugins/css/CSSClassDefinitionLocator.ts +++ b/packages/language-server/src/plugins/typescript/CSSClassDefinitionLocator.ts @@ -1,7 +1,7 @@ import { Position, Range } from 'vscode-languageserver'; -import { SvelteDocumentSnapshot } from '../typescript/DocumentSnapshot'; +import { SvelteDocumentSnapshot } from './DocumentSnapshot'; import { Document } from '../../lib/documents'; -import { SvelteNode } from '../typescript/svelte-ast-utils'; +import { SvelteNode } from './svelte-ast-utils'; export class CSSClassDefinitionLocator { initialNodeAt: SvelteNode; constructor( @@ -12,17 +12,17 @@ export class CSSClassDefinitionLocator { this.initialNodeAt = this.tsDoc.svelteNodeAt(this.position) as SvelteNode; } - public GetCSSClassDefinition() { - if (this.IsStandardClassFormat()) { - return this.GetStandardFormatClassName(); + getCSSClassDefinition() { + if (this.isStandardClassFormat()) { + return this.getStandardFormatClassName(); } - if (this.IsDirectiveFormat() && this.initialNodeAt.name) { - return this.GetDefinitionRangeWithinStyleSection(`.${this.initialNodeAt.name}`); + if (this.isDirectiveFormat() && this.initialNodeAt.name) { + return this.getDefinitionRangeWithinStyleSection(`.${this.initialNodeAt.name}`); } - if (this.IsConditionalExpressionClassFormat() && this.initialNodeAt.value) { - return this.GetDefinitionRangeWithinStyleSection(`.${this.initialNodeAt.value}`); + if (this.isConditionalExpressionClassFormat() && this.initialNodeAt.value) { + return this.getDefinitionRangeWithinStyleSection(`.${this.initialNodeAt.value}`); } return false; @@ -32,7 +32,7 @@ export class CSSClassDefinitionLocator { * Standard format: * class="test test1" */ - public IsStandardClassFormat() { + private isStandardClassFormat() { if (this.initialNodeAt?.type == 'Text' && this.initialNodeAt?.parent?.name == 'class') { return true; } @@ -44,7 +44,7 @@ export class CSSClassDefinitionLocator { * Conditional Expression format: * class="{current === 'baz' ? 'selected' : '' */ - public IsConditionalExpressionClassFormat() { + private isConditionalExpressionClassFormat() { if ( this.initialNodeAt?.type == 'Literal' && this.initialNodeAt?.parent?.type == 'ConditionalExpression' && @@ -60,7 +60,7 @@ export class CSSClassDefinitionLocator { * Class Directive format: * class:active="{current === 'foo'}" */ - public IsDirectiveFormat() { + private isDirectiveFormat() { if (this.initialNodeAt?.type == 'Class' && this.initialNodeAt?.parent?.type == 'Element') { return true; } @@ -68,7 +68,7 @@ export class CSSClassDefinitionLocator { return false; } - public GetStandardFormatClassName() { + private getStandardFormatClassName() { const testEndTagRange = Range.create( Position.create(this.position.line, 0), Position.create(this.position.line, this.position.character) @@ -100,14 +100,14 @@ export class CSSClassDefinitionLocator { const cssClassName = this.initialNodeAt?.data.split(' ')[spaceCount]; - return this.GetDefinitionRangeWithinStyleSection(`.${cssClassName}`); + return this.getDefinitionRangeWithinStyleSection(`.${cssClassName}`); } - public GetDefinitionRangeWithinStyleSection(targetClass: string) { + private getDefinitionRangeWithinStyleSection(targetClass: string) { let indexOccurence = this.document.content.indexOf(targetClass, 0); while (indexOccurence >= 0) { - if (this.IsOffsetWithinStyleSection(indexOccurence)) { + if (this.isOffsetWithinStyleSection(indexOccurence)) { const startPosition = this.document.positionAt(indexOccurence); const targetRange = Range.create( startPosition, @@ -118,7 +118,7 @@ export class CSSClassDefinitionLocator { ); indexOccurence = this.document.content.indexOf(targetClass, indexOccurence + 1); - if (!this.IsExactClassMatch(targetRange)) { + if (!this.isExactClassMatch(targetRange)) { continue; } @@ -127,7 +127,7 @@ export class CSSClassDefinitionLocator { } } - public IsOffsetWithinStyleSection(offsetPosition: number) { + private isOffsetWithinStyleSection(offsetPosition: number) { if (this.document.styleInfo) { if ( offsetPosition > this.document.styleInfo?.start && @@ -140,7 +140,7 @@ export class CSSClassDefinitionLocator { return false; } - public IsExactClassMatch(testRange: Range) { + private isExactClassMatch(testRange: Range) { //Check nothing before the test position if (testRange.start.character > 0) { const beforerange = Range.create( diff --git a/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts b/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts index 90abe9cb8..999ca4683 100644 --- a/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts +++ b/packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts @@ -26,7 +26,7 @@ import { import { Document, getTextInRange, mapSymbolInformationToOriginal } from '../../lib/documents'; import { LSConfigManager, LSTypescriptConfig } from '../../ls-config'; import { isNotNullOrUndefined, isZeroLengthRange, pathToUrl } from '../../utils'; -import { CSSClassDefinitionLocator } from '../css/CSSClassDefinitionLocator'; +import { CSSClassDefinitionLocator } from './CSSClassDefinitionLocator'; import { AppCompletionItem, AppCompletionList, @@ -331,7 +331,7 @@ export class TypeScriptPlugin const mainFragment = tsDoc.getFragment(); const cssClassHelper = new CSSClassDefinitionLocator(tsDoc, position, document); - const cssDefinitionRange = cssClassHelper.GetCSSClassDefinition(); + const cssDefinitionRange = cssClassHelper.getCSSClassDefinition(); if (cssDefinitionRange) { const results: DefinitionLink[] = []; cssDefinitionRange.start.character++; //Report start of name instead of start at . for easy rename (F2) possibilities From 7f86d5ca3e3ab4c46cdf410b0478716e063025dc Mon Sep 17 00:00:00 2001 From: Jojoshua Date: Tue, 14 Jun 2022 19:35:17 -0400 Subject: [PATCH 6/7] Improve css combination termination cases --- .../src/plugins/typescript/CSSClassDefinitionLocator.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/language-server/src/plugins/typescript/CSSClassDefinitionLocator.ts b/packages/language-server/src/plugins/typescript/CSSClassDefinitionLocator.ts index 88aceebce..e0f062fd9 100644 --- a/packages/language-server/src/plugins/typescript/CSSClassDefinitionLocator.ts +++ b/packages/language-server/src/plugins/typescript/CSSClassDefinitionLocator.ts @@ -4,6 +4,7 @@ import { Document } from '../../lib/documents'; import { SvelteNode } from './svelte-ast-utils'; export class CSSClassDefinitionLocator { initialNodeAt: SvelteNode; + cssClassTerminators = ['', '{', '.', '>', '~', '>', '[', ':', '#', '+']; constructor( public tsDoc: SvelteDocumentSnapshot, public position: Position, @@ -152,13 +153,13 @@ export class CSSClassDefinitionLocator { } } - //Check space or { is after the test position + //Check css terminator is after the test position const afterRange = Range.create( Position.create(testRange.end.line, testRange.end.character), Position.create(testRange.end.line, testRange.end.character + 1) ); const afterRangeText = this.document.getText(afterRange).trim(); - if (afterRangeText == '' || afterRangeText == '{') { + if (this.cssClassTerminators.includes(afterRangeText)) { return true; } From 601023610bca627a07c52dfcca877921ddb5cb08 Mon Sep 17 00:00:00 2001 From: Jojoshua Date: Wed, 21 Dec 2022 14:01:09 -0500 Subject: [PATCH 7/7] Add support for navigating to element tagname --- .../typescript/CSSClassDefinitionLocator.ts | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/language-server/src/plugins/typescript/CSSClassDefinitionLocator.ts b/packages/language-server/src/plugins/typescript/CSSClassDefinitionLocator.ts index e0f062fd9..b1a08066e 100644 --- a/packages/language-server/src/plugins/typescript/CSSClassDefinitionLocator.ts +++ b/packages/language-server/src/plugins/typescript/CSSClassDefinitionLocator.ts @@ -18,6 +18,10 @@ export class CSSClassDefinitionLocator { return this.getStandardFormatClassName(); } + if (this.isTargetHTMLElement()) { + return this.getHTMlElementName(); + } + if (this.isDirectiveFormat() && this.initialNodeAt.name) { return this.getDefinitionRangeWithinStyleSection(`.${this.initialNodeAt.name}`); } @@ -41,6 +45,18 @@ export class CSSClassDefinitionLocator { return false; } + /** + * HTML Element target: + *
+ */ + private isTargetHTMLElement() { + if (this.initialNodeAt?.type == 'Element') { + return true; + } + + return false; + } + /** * Conditional Expression format: * class="{current === 'baz' ? 'selected' : '' @@ -104,8 +120,25 @@ export class CSSClassDefinitionLocator { return this.getDefinitionRangeWithinStyleSection(`.${cssClassName}`); } + private getHTMlElementName() { + const result = this.getDefinitionRangeWithinStyleSection(`${this.initialNodeAt.name}`); + if (result) { + //Shift start/end to get the highlight right + const originalStart = result.start.character; + result.start.character -= 1; + if (this.initialNodeAt.name) { + result.end.character = originalStart + this.initialNodeAt.name.length; + } + + return result; + } + } + private getDefinitionRangeWithinStyleSection(targetClass: string) { - let indexOccurence = this.document.content.indexOf(targetClass, 0); + let indexOccurence = this.document.content.indexOf( + targetClass, + this.document.styleInfo?.start + ); while (indexOccurence >= 0) { if (this.isOffsetWithinStyleSection(indexOccurence)) { @@ -124,6 +157,8 @@ export class CSSClassDefinitionLocator { } return targetRange; + } else { + break; } } }