From 8a4fd628798d1d32a7efb79c3544b6bd9f3154f1 Mon Sep 17 00:00:00 2001 From: Julio Ortega Date: Mon, 11 Nov 2024 19:54:42 -0600 Subject: [PATCH 1/3] WIP Signed-off-by: Julio Ortega --- .eslintignore | 3 + .../server/src/language-plugin.ts | 2 +- .../server/src/resolvers/css-variables.ts | 4 +- .../server/src/resolvers/vue-components.ts | 101 +++++++++++++----- .../src/services/dialtone-components.ts | 42 +++++--- .../server/src/services/dialtone-tokens.ts | 62 +++++++---- packages/language-server/server/src/utils.ts | 15 ++- 7 files changed, 162 insertions(+), 67 deletions(-) diff --git a/.eslintignore b/.eslintignore index d038871fb4..7728a6deb9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,3 +5,6 @@ **/components/emoji_text_wrapper/emoji_text_wrapper.test.js **/components/icon/icon_constants.js **/components/illustration/illustration_constants.js + +# Language server test files +packages/language-server/sample/* \ No newline at end of file diff --git a/packages/language-server/server/src/language-plugin.ts b/packages/language-server/server/src/language-plugin.ts index 2e997f3d99..0291e06af2 100644 --- a/packages/language-server/server/src/language-plugin.ts +++ b/packages/language-server/server/src/language-plugin.ts @@ -38,7 +38,7 @@ export class DialtoneVirtualCode implements VirtualCode { lengths: [this.snapshot.getLength()], data: { completion: true, - // semantic: true, + semantic: true, }, }]; } diff --git a/packages/language-server/server/src/resolvers/css-variables.ts b/packages/language-server/server/src/resolvers/css-variables.ts index ad02dff9e8..b79fd64cfe 100644 --- a/packages/language-server/server/src/resolvers/css-variables.ts +++ b/packages/language-server/server/src/resolvers/css-variables.ts @@ -63,8 +63,6 @@ function processDocumentation(docs: DialtoneTokensDoc) { value: documentation, } as MarkupContent; - variable.detail = variable.detail || 'Missing variable description'; - variablesDocumentation.push(variable) } @@ -74,7 +72,7 @@ function processDocumentation(docs: DialtoneTokensDoc) { const tokensDocumentation: DialtoneTokensDoc = require('../../node_modules/@dialpad/dialtone-tokens/dist/doc.json'); // @TODO: Process the tokens on build, as it is a static file that will not change on runtime. -const cssVariablesDocumentation: CompletionItem[] = processDocumentation(tokensDocumentation); +export const cssVariablesDocumentation: CompletionItem[] = processDocumentation(tokensDocumentation); export function resolveCSSVariables(currentWord: string): NullableProviderResult { console.log('Resolving CSS Variables', currentWord); diff --git a/packages/language-server/server/src/resolvers/vue-components.ts b/packages/language-server/server/src/resolvers/vue-components.ts index 72e4a6e590..81380cf9b5 100644 --- a/packages/language-server/server/src/resolvers/vue-components.ts +++ b/packages/language-server/server/src/resolvers/vue-components.ts @@ -1,6 +1,6 @@ import type { CompletionContext, CompletionItem, CompletionList, NullableProviderResult } from "@volar/language-server/node"; -import { CompletionItemKind } from "@volar/language-server/node"; -import { stringToHumanReadable, stringToKebabCase } from "../utils"; +import { Command, CompletionItemKind } from "@volar/language-server/node"; +import { getCurrentWord, stringToHumanReadable, stringToKebabCase } from "../utils"; export type DialtoneComponentDoc = { displayName: string; @@ -31,17 +31,15 @@ export const components = componentDocumentation.map((component: DialtoneCompone const componentName = stringToKebabCase(component.displayName); const humanReadableName = stringToHumanReadable(component.displayName); return { - label: componentName, + label: `${componentName} `, kind: CompletionItemKind.Text, detail: humanReadableName, documentation: component.description, - deprecated: component.deprecated + deprecated: component.deprecated, } satisfies CompletionItem; }) satisfies CompletionItem[]; -export function resolveVueComponents(currentLine: string, currentWord: string, sanitizedWord: string, context: CompletionContext): NullableProviderResult { - console.log('Resolving Vue Components', currentLine, currentWord, sanitizedWord, context); - +export function resolveVueComponents(currentLine: string, currentWord: string): NullableProviderResult { // Get the clean tag-name const tagName = currentLine.replace(/\s+<([\w-]+).*/, '$1'); @@ -49,23 +47,22 @@ export function resolveVueComponents(currentLine: string, currentWord: string, s stringToKebabCase(component.displayName) === tagName ); - if (currentWord.trim().startsWith('<') || context.triggerCharacter === '<') { - return { isIncomplete: false, items: components } - } - if (!component) return; - const propValues = component.props - .find(prop => stringToKebabCase(prop.name) === stringToKebabCase(sanitizedWord)) - ?.values - ?.map(val => ({ - label: val, - kind: CompletionItemKind.Value, - }) as CompletionItem); + if (currentLine.endsWith('"')) { + const propValues = component.props + .find(prop => stringToKebabCase(prop.name) === stringToKebabCase(currentWord)) + ?.values + ?.map(val => ({ + label: val, + kind: CompletionItemKind.Value, + }) as CompletionItem); - if (propValues?.length) { - return { isIncomplete: false, items: propValues } + if (propValues?.length) { + console.log('Resolving values', currentWord); + return { isIncomplete: false, items: propValues } + } } const props = component.props @@ -78,10 +75,66 @@ export function resolveVueComponents(currentLine: string, currentWord: string, s detail: `Default: ${prop.defaultValue?.value}`, documentation: prop.description }) as CompletionItem) - .filter(item => - // @TODO: Filter properties that are already set - item.label.startsWith(sanitizedWord) - ); + .filter(item => { + console.log(item.label, currentWord); + + return item.label.startsWith(currentWord) + }); + console.log('Resolving properties', currentWord); return { isIncomplete: false, items: props }; } + +export function resolveComponentProps(currentLine: string, currentWord: string): NullableProviderResult { + // Get the clean tag-name + const tagName = currentLine.replace(/\s+<([\w-]+).*/, '$1'); + + const component = componentDocumentation.find(component => + stringToKebabCase(component.displayName) === tagName + ); + + if (!component) + return; + + const props = component.props + .map(prop => ({ + label: stringToKebabCase(prop.name), + kind: CompletionItemKind.Field, + labelDetails: { + detail: `: ${prop.type.name}`, + }, + detail: `Default: ${prop.defaultValue?.value}`, + documentation: prop.description, + }) as CompletionItem); + + console.log('Resolving properties', currentWord); + return { isIncomplete: false, items: props }; +} + +export function resolvePropValues(currentLine: string, currentWord: string): NullableProviderResult { + console.log('Resolving values', currentWord); + + // Get the clean tag-name + const tagName = currentLine.replace(/\s+<([\w-]+).*/, '$1'); + + const component = componentDocumentation.find(component => + stringToKebabCase(component.displayName) === tagName + ); + + if (!component) + return; + + const prop = component.props.find(prop => stringToKebabCase(prop.name) === stringToKebabCase(currentWord)); + + if (!prop) return; + + const propValues = prop.values?.map(val => ({ + label: val, + kind: CompletionItemKind.Value, + }) as CompletionItem); + + if (propValues?.length) { + console.log('Resolving values', currentWord); + return { isIncomplete: false, items: propValues } + } +} diff --git a/packages/language-server/server/src/services/dialtone-components.ts b/packages/language-server/server/src/services/dialtone-components.ts index b6090ecab7..13d4e73cb8 100644 --- a/packages/language-server/server/src/services/dialtone-components.ts +++ b/packages/language-server/server/src/services/dialtone-components.ts @@ -1,5 +1,5 @@ import type { LanguageServicePlugin, LanguageServicePluginInstance } from "@volar/language-service"; -import { resolveVueComponents } from "../resolvers/vue-components"; +import { components, resolveComponentProps, resolvePropValues, resolveVueComponents } from "../resolvers/vue-components"; import { getContent, getCurrentWord } from "../utils"; export type DialtoneTokenDoc = { @@ -20,44 +20,52 @@ export function create(): LanguageServicePlugin { completionProvider: { triggerCharacters: ['<', '\:', '"', '\''], }, - // hoverProvider: true, + hoverProvider: true, }, create(context): LanguageServicePluginInstance { console.log('Created Dialtone Components service'); return { provideCompletionItems(document, position, completionContext) { - console.log('Providing Component Completion Items'); const content = getContent(document, context); if (!content) return; const currentLine: string = content.split('\n')[position.line]; - const currentWord = getCurrentWord(currentLine, position); - - // Remove all the trigger character from current word - const sanitizedWord = currentWord.replaceAll(/[<="'\:]/g, ''); // @TODO: Find multi-line components if (!currentLine.includes('= matchStart && position.character <= matchEnd); - // console.log('hovering: ', currentWord); + if (!isHoveringVariable) return; + const variableName = variableMatch[0]; - // return { contents: { kind: "plaintext", value: 'Hover CSS content' } }; - // }, + console.log('Hovering: ', variableName); + + const cssVariable = cssVariablesDocumentation.find(item => item.label === variableName); + if (!cssVariable) return; + + return { + contents: cssVariable.documentation as MarkupContent, + range: { + start: { + line: position.line, + character: matchStart, + }, + end: { + line: position.line, + character: matchEnd, + } + } + }; + }, }; }, } diff --git a/packages/language-server/server/src/utils.ts b/packages/language-server/server/src/utils.ts index 38035bd24a..e49ad2b427 100644 --- a/packages/language-server/server/src/utils.ts +++ b/packages/language-server/server/src/utils.ts @@ -1,5 +1,5 @@ import type { LanguageServiceContext } from "@volar/language-server"; -import type { Position, TextDocument } from "vscode-html-languageservice"; +import type { TextDocument } from "vscode-html-languageservice"; import { URI } from "vscode-uri"; import { DialtoneVirtualCode } from "./language-plugin"; @@ -11,8 +11,17 @@ export function stringToHumanReadable(string: string): string { return string.split(/(?=[A-Z]|[0-9]{3,}?)/).join(' ') } -export function getCurrentWord(line: string, position: Position): string { - return line.slice(line.lastIndexOf(' ', position.character), position.character).trim(); +export function getCurrentWord(line: string, offset: number): string { + let wordStart = line.lastIndexOf(' ', offset); + if (wordStart === -1) wordStart = 0; + + let wordEnd = line.indexOf(' ', offset); + if (wordEnd === -1) wordEnd = line.length; + + const word = line.slice(wordStart, wordEnd); + + // Removes all the non-word characters and the beginning and end of the current word. + return word.replace(/^[^\w-]*([\w-]+)[^\w-]*$/gi, '$1').trim(); } export function getContent(document: TextDocument, context: LanguageServiceContext): string | undefined { From fbce0677f345e634e562f44974ec38edf02616b8 Mon Sep 17 00:00:00 2001 From: Julio Ortega Date: Thu, 14 Nov 2024 13:49:02 -0600 Subject: [PATCH 2/3] add hover provider --- .gitignore | 2 + packages/language-server/README.md | 12 ++- packages/language-server/sample/README.md | 11 +++ packages/language-server/sample/test.vue | 10 --- packages/language-server/sample/test2.css | 3 - .../server/src/resolvers/css-variables.ts | 8 +- .../server/src/resolvers/vue-components.ts | 77 +++--------------- .../src/services/dialtone-components.ts | 81 ++++++++++++++----- .../server/src/services/dialtone-tokens.ts | 7 +- packages/language-server/server/src/utils.ts | 31 +++++-- 10 files changed, 127 insertions(+), 115 deletions(-) create mode 100644 packages/language-server/sample/README.md delete mode 100644 packages/language-server/sample/test.vue delete mode 100644 packages/language-server/sample/test2.css diff --git a/.gitignore b/.gitignore index b2f2e34b7a..a61f223f4f 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,5 @@ packages/postcss-responsive-variations/coverage # Language Server *.tsbuildinfo *.vsix +packages/language-server/sample/*.vue +packages/language-server/sample/*.css diff --git a/packages/language-server/README.md b/packages/language-server/README.md index d8f42d8ea3..650cd15ff3 100644 --- a/packages/language-server/README.md +++ b/packages/language-server/README.md @@ -25,10 +25,14 @@ This is the Dialtone language tools based on Volar Framework. - Switch to the Debug viewlet. - Select `Launch Client` from the drop down. - Run the launch config. -- In the [Extension Development Host] instance of VSCode, open a `test.vue` - - Type `` to try property completion. - - Have `` to see values completion. +- The [Extension Development Host] instance of VSCode, will open the `sample` folder. + - On a `.vue` file: + - Type `` to trigger property completion. + - Type `` to trigger values completion. + - On a `.css` file: + - Type `color: var(--dt-|)` to trigger token completion +- If no completion is provided automatically (depends on your VSCode config), press `Ctrl + Space` to trigger the completions. ## Build .vsix diff --git a/packages/language-server/sample/README.md b/packages/language-server/sample/README.md new file mode 100644 index 0000000000..501c53fb9e --- /dev/null +++ b/packages/language-server/sample/README.md @@ -0,0 +1,11 @@ +Create or open a `.vue` or a `.css` file to start testing the extension. + +- On a `.vue` file: + - Type `` to trigger property completion. + - Type `` to trigger values completion. + +- On a `.css` file: + - Type `color: var(--dt-|)` to trigger token completion + +- If no completion is provided automatically (depends on your VSCode config), press `Ctrl + Space` to trigger the completions. diff --git a/packages/language-server/sample/test.vue b/packages/language-server/sample/test.vue deleted file mode 100644 index 10b61d7f4d..0000000000 --- a/packages/language-server/sample/test.vue +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/packages/language-server/sample/test2.css b/packages/language-server/sample/test2.css deleted file mode 100644 index 0f00531b0a..0000000000 --- a/packages/language-server/sample/test2.css +++ /dev/null @@ -1,3 +0,0 @@ -.custom-color { - color: var() -} diff --git a/packages/language-server/server/src/resolvers/css-variables.ts b/packages/language-server/server/src/resolvers/css-variables.ts index b79fd64cfe..6fdb0df917 100644 --- a/packages/language-server/server/src/resolvers/css-variables.ts +++ b/packages/language-server/server/src/resolvers/css-variables.ts @@ -32,7 +32,7 @@ function getItemKind(itemName: string): CompletionItemKind { } function processDocumentation(docs: DialtoneTokensDoc) { - const themeNames = Object.keys(docs); + const themeNames = Object.keys(docs).filter(themeName => ['dp-light', 'base-light', 'dp-dark', 'base-dark'].includes(themeName)); const variableNames = new Set(themeNames.map(themeName => Object.keys(docs[themeName])).flat()) const variablesDocumentation: CompletionItem[] = []; @@ -50,9 +50,9 @@ function processDocumentation(docs: DialtoneTokensDoc) { variable.detail = cssVariable.description || variable.detail; // Small text to the right of the variable label - // if (!variable.labelDetails) { - // variable.labelDetails = { description: cssVariable.value }; - // } + if (!variable.labelDetails) { + variable.labelDetails = { description: cssVariable.value }; + } documentation += `- **${themeName}**: ${cssVariable.value}\n`; diff --git a/packages/language-server/server/src/resolvers/vue-components.ts b/packages/language-server/server/src/resolvers/vue-components.ts index 81380cf9b5..5168463177 100644 --- a/packages/language-server/server/src/resolvers/vue-components.ts +++ b/packages/language-server/server/src/resolvers/vue-components.ts @@ -1,6 +1,6 @@ -import type { CompletionContext, CompletionItem, CompletionList, NullableProviderResult } from "@volar/language-server/node"; -import { Command, CompletionItemKind } from "@volar/language-server/node"; -import { getCurrentWord, stringToHumanReadable, stringToKebabCase } from "../utils"; +import type { CompletionItem, CompletionList, NullableProviderResult } from "@volar/language-server/node"; +import { CompletionItemKind } from "@volar/language-server/node"; +import { stringToHumanReadable, stringToKebabCase } from "../utils"; export type DialtoneComponentDoc = { displayName: string; @@ -25,13 +25,13 @@ export type DialtoneComponentDoc = { }[] }; -const componentDocumentation: DialtoneComponentDoc[] = require('../../node_modules/@dialpad/dialtone-vue/dist/component-documentation.json'); +export const componentDocumentation: DialtoneComponentDoc[] = require('../../node_modules/@dialpad/dialtone-vue/dist/component-documentation.json'); export const components = componentDocumentation.map((component: DialtoneComponentDoc) => { const componentName = stringToKebabCase(component.displayName); const humanReadableName = stringToHumanReadable(component.displayName); return { - label: `${componentName} `, + label: componentName, kind: CompletionItemKind.Text, detail: humanReadableName, documentation: component.description, @@ -39,56 +39,7 @@ export const components = componentDocumentation.map((component: DialtoneCompone } satisfies CompletionItem; }) satisfies CompletionItem[]; -export function resolveVueComponents(currentLine: string, currentWord: string): NullableProviderResult { - // Get the clean tag-name - const tagName = currentLine.replace(/\s+<([\w-]+).*/, '$1'); - - const component = componentDocumentation.find(component => - stringToKebabCase(component.displayName) === tagName - ); - - if (!component) - return; - - if (currentLine.endsWith('"')) { - const propValues = component.props - .find(prop => stringToKebabCase(prop.name) === stringToKebabCase(currentWord)) - ?.values - ?.map(val => ({ - label: val, - kind: CompletionItemKind.Value, - }) as CompletionItem); - - if (propValues?.length) { - console.log('Resolving values', currentWord); - return { isIncomplete: false, items: propValues } - } - } - - const props = component.props - .map(prop => ({ - label: stringToKebabCase(prop.name), - kind: CompletionItemKind.Field, - labelDetails: { - detail: `: ${prop.type.name}`, - }, - detail: `Default: ${prop.defaultValue?.value}`, - documentation: prop.description - }) as CompletionItem) - .filter(item => { - console.log(item.label, currentWord); - - return item.label.startsWith(currentWord) - }); - - console.log('Resolving properties', currentWord); - return { isIncomplete: false, items: props }; -} - -export function resolveComponentProps(currentLine: string, currentWord: string): NullableProviderResult { - // Get the clean tag-name - const tagName = currentLine.replace(/\s+<([\w-]+).*/, '$1'); - +export function resolveComponentProps(tagName: string, currentWord: string): NullableProviderResult { const component = componentDocumentation.find(component => stringToKebabCase(component.displayName) === tagName ); @@ -97,6 +48,7 @@ export function resolveComponentProps(currentLine: string, currentWord: string): return; const props = component.props + .filter(prop => stringToKebabCase(prop.name).startsWith(currentWord)) .map(prop => ({ label: stringToKebabCase(prop.name), kind: CompletionItemKind.Field, @@ -107,16 +59,11 @@ export function resolveComponentProps(currentLine: string, currentWord: string): documentation: prop.description, }) as CompletionItem); - console.log('Resolving properties', currentWord); - return { isIncomplete: false, items: props }; + console.info('Resolving props'); + return { isIncomplete: true, items: props }; } -export function resolvePropValues(currentLine: string, currentWord: string): NullableProviderResult { - console.log('Resolving values', currentWord); - - // Get the clean tag-name - const tagName = currentLine.replace(/\s+<([\w-]+).*/, '$1'); - +export function resolvePropValues(tagName: string, propName: string): NullableProviderResult { const component = componentDocumentation.find(component => stringToKebabCase(component.displayName) === tagName ); @@ -124,7 +71,7 @@ export function resolvePropValues(currentLine: string, currentWord: string): Nul if (!component) return; - const prop = component.props.find(prop => stringToKebabCase(prop.name) === stringToKebabCase(currentWord)); + const prop = component.props.find(prop => stringToKebabCase(prop.name) === stringToKebabCase(propName)); if (!prop) return; @@ -134,7 +81,7 @@ export function resolvePropValues(currentLine: string, currentWord: string): Nul }) as CompletionItem); if (propValues?.length) { - console.log('Resolving values', currentWord); + console.info('Resolving values'); return { isIncomplete: false, items: propValues } } } diff --git a/packages/language-server/server/src/services/dialtone-components.ts b/packages/language-server/server/src/services/dialtone-components.ts index 13d4e73cb8..28b8ca54ad 100644 --- a/packages/language-server/server/src/services/dialtone-components.ts +++ b/packages/language-server/server/src/services/dialtone-components.ts @@ -1,6 +1,6 @@ import type { LanguageServicePlugin, LanguageServicePluginInstance } from "@volar/language-service"; -import { components, resolveComponentProps, resolvePropValues, resolveVueComponents } from "../resolvers/vue-components"; -import { getContent, getCurrentWord } from "../utils"; +import { componentDocumentation, components, resolveComponentProps, resolvePropValues } from "../resolvers/vue-components"; +import { getContent, getCurrentWord, stringToKebabCase, wordUnderCursor } from "../utils"; export type DialtoneTokenDoc = { [theme: string]: { @@ -18,7 +18,7 @@ export function create(): LanguageServicePlugin { name: "dialtone-components", capabilities: { completionProvider: { - triggerCharacters: ['<', '\:', '"', '\''], + triggerCharacters: ['\:', '"', '\''], }, hoverProvider: true, }, @@ -26,8 +26,7 @@ export function create(): LanguageServicePlugin { console.log('Created Dialtone Components service'); return { - provideCompletionItems(document, position, completionContext) { - + provideCompletionItems(document, position) { const content = getContent(document, context); if (!content) return; @@ -37,16 +36,30 @@ export function create(): LanguageServicePlugin { if (!currentLine.includes('= quotesMatch.index + quotesMatch[0].length) + continue; - if (currentWord.startsWith('dt-')) { - console.log('Resolving components', currentWord); + const propName = currentLine.replace(/.*[^\w-](.*?)="[\w-]*".*/, "$1") + console.log('prop name: ', propName); + + return resolvePropValues(tagName, propName) + } + + if (/^\ stringToKebabCase(component.displayName) === tagName) - console.log('Providing Token Hover'); - const currentWord = getCurrentWord(currentLine, position.character); + if (!component) return; - console.log('hovering: ', currentWord); + console.info(`Component hover context (current-word: ${currentWord.text}, tag-name: ${tagName})`); + if (tagName === currentWord.text) { + + if (!component.description) return; + + return { + contents: { + kind: 'markdown', + value: component.description + }, + range: currentWord.range + }; + } + + const prop = component.props.find(prop => stringToKebabCase(prop.name) === stringToKebabCase(currentWord.text)); + + if (!prop || !prop.description) return; + + let description = prop.description; + if (prop.values) { + description += `\n\n**Values**: [${prop.values.join(', ')}]\n` + } + if (prop.defaultValue?.value) { + description += `\n\n**Default**: ${prop.defaultValue.value}\n` + } - return { contents: ['Hover Component content'] }; + return { + contents: { + kind: 'markdown', + value: description + }, + range: currentWord.range + }; }, }; }, diff --git a/packages/language-server/server/src/services/dialtone-tokens.ts b/packages/language-server/server/src/services/dialtone-tokens.ts index ba9aeaac73..4571254fdf 100644 --- a/packages/language-server/server/src/services/dialtone-tokens.ts +++ b/packages/language-server/server/src/services/dialtone-tokens.ts @@ -35,9 +35,10 @@ export function create(): LanguageServicePlugin { if (!currentLine.includes('var(--dt-')) return; - console.log('Providing Tokens Completion Items'); - const currentWord = getCurrentWord(currentLine, position.character); + + console.info(`Token completion context (current-word: ${currentWord})`); + return resolveCSSVariables(currentWord); }, @@ -61,7 +62,7 @@ export function create(): LanguageServicePlugin { const variableName = variableMatch[0]; - console.log('Hovering: ', variableName); + console.info(`Token hover context (current-word: ${variableName}, start: ${matchStart}, end: ${matchEnd})`); const cssVariable = cssVariablesDocumentation.find(item => item.label === variableName); if (!cssVariable) return; diff --git a/packages/language-server/server/src/utils.ts b/packages/language-server/server/src/utils.ts index e49ad2b427..04adb8363c 100644 --- a/packages/language-server/server/src/utils.ts +++ b/packages/language-server/server/src/utils.ts @@ -1,4 +1,4 @@ -import type { LanguageServiceContext } from "@volar/language-server"; +import type { LanguageServiceContext, Position, Range } from "@volar/language-server"; import type { TextDocument } from "vscode-html-languageservice"; import { URI } from "vscode-uri"; import { DialtoneVirtualCode } from "./language-plugin"; @@ -12,16 +12,31 @@ export function stringToHumanReadable(string: string): string { } export function getCurrentWord(line: string, offset: number): string { - let wordStart = line.lastIndexOf(' ', offset); - if (wordStart === -1) wordStart = 0; + const lineUntilCursor = line.slice(0, offset); + return lineUntilCursor.replace(/.*[^\w-](.*?)/, "$1"); +} + +export function wordUnderCursor(content: string, position: Position) { + const currentLine: string = content.split('\n')[position.line]; + + if (!currentLine.includes(' Date: Mon, 18 Nov 2024 14:38:30 -0600 Subject: [PATCH 3/3] add language-server to release process Signed-off-by: Julio Ortega --- .github/workflows/release.yml | 5 ++ packages/language-server/project.json | 12 ++-- .../server/release-ci.config.cjs | 54 ++++++++++++++++++ packages/language-server/vscode/package.json | 2 +- .../vscode/release-ci.config.cjs | 55 +++++++++++++++++++ 5 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 packages/language-server/server/release-ci.config.cjs create mode 100644 packages/language-server/vscode/release-ci.config.cjs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f63c495c7e..8fb042fa0b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,7 @@ on: - css - emojis - icons + - language-server - tokens - vue2 - vue3 @@ -116,6 +117,10 @@ jobs: if: ${{ github.event_name == 'schedule' || github.event.inputs.package == 'all' || github.event.inputs.package == 'icons' }} run: pnpm nx run dialtone-icons:release + - name: Release Dialtone Language server ${{ env.RELEASE_TAG }} + if: ${{ github.event_name == 'schedule' || github.event.inputs.package == 'all' || github.event.inputs.package == 'language-server' }} + run: pnpm nx run dialtone-language-server:release + - name: Release Dialtone Tokens ${{ env.RELEASE_TAG }} if: ${{ github.event_name == 'schedule' || github.event.inputs.package == 'all' || github.event.inputs.package == 'tokens' }} run: pnpm nx run dialtone-tokens:release diff --git a/packages/language-server/project.json b/packages/language-server/project.json index 19f0361941..cc3ab00892 100644 --- a/packages/language-server/project.json +++ b/packages/language-server/project.json @@ -16,12 +16,6 @@ "command": "pnpm run build" } }, - "publish": { - "executor": "nx:run-commands", - "options": { - "command": "pnpm publish --filter ./packages/dialtone-vue2" - } - }, "pack": { "executor": "nx:run-commands", "dependsOn": [ @@ -36,8 +30,12 @@ }, "release": { "executor": "nx:run-commands", + "dependsOn": [ "pack" ], "options": { - "command": "pnpm semantic-release-plus --extends ./packages/dialtone-vue2/release-ci.config.cjs && sleep 3", + "commands": [ + "pnpm semantic-release-plus --extends ./packages/language-server/server/release-ci.config.cjs && sleep 3", + "pnpm semantic-release-plus --extends ./packages/language-server/vscode/release-ci.config.cjs && sleep 3" + ], "parallel": false } } diff --git a/packages/language-server/server/release-ci.config.cjs b/packages/language-server/server/release-ci.config.cjs new file mode 100644 index 0000000000..6f55dce601 --- /dev/null +++ b/packages/language-server/server/release-ci.config.cjs @@ -0,0 +1,54 @@ +/* eslint-disable no-template-curly-in-string */ +const name = 'language-server'; +const srcRoot = `packages/language-server/${name}`; + +/** + * @type {import('semantic-release').GlobalConfig} + */ +module.exports = { + pkgRoot: srcRoot, + tagFormat: name + '/v${version}', + commitPaths: [`${srcRoot}/*`], + plugins: [ + ['@semantic-release/commit-analyzer', { + preset: 'angular', + releaseRules: [ + { type: 'refactor', release: 'patch' }, + ], + }], + ['@semantic-release/release-notes-generator', { + config: '@dialpad/conventional-changelog-angular', + }], + ['@dialpad/semantic-release-changelog-json', { + changelogFile: `${srcRoot}/CHANGELOG.md`, + changelogJsonFile: `${srcRoot}/CHANGELOG.json`, + }], + ['@semantic-release/changelog', { changelogFile: `${srcRoot}/CHANGELOG.md` }], + ['@semantic-release/npm', { npmPublish: false }], + ['@semantic-release/git', { + assets: [ + `${srcRoot}/CHANGELOG.md`, + `${srcRoot}/CHANGELOG.json`, + `${srcRoot}/package.json`, + ], + message: `chore(release): NO-JIRA ${name}` + + '/v${nextRelease.version}\n\n${nextRelease.notes}', + }], + ['@semantic-release/github', { + successComment: false, + failTitle: false, + }], + ], + branches: [ + 'staging', + 'next', + { + name: 'beta', + prerelease: true, + }, + { + name: 'alpha', + prerelease: true, + }, + ], +}; diff --git a/packages/language-server/vscode/package.json b/packages/language-server/vscode/package.json index 473b11da78..acabf9bf33 100644 --- a/packages/language-server/vscode/package.json +++ b/packages/language-server/vscode/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "vscode-dialtone", - "version": "1.0.0-alpha.2", + "version": "1.0.0-alpha.3", "categories": [ "Other" ], diff --git a/packages/language-server/vscode/release-ci.config.cjs b/packages/language-server/vscode/release-ci.config.cjs new file mode 100644 index 0000000000..bb8f2ab9f0 --- /dev/null +++ b/packages/language-server/vscode/release-ci.config.cjs @@ -0,0 +1,55 @@ +/* eslint-disable no-template-curly-in-string */ +const name = 'vscode'; +const srcRoot = `packages/language-server/${name}`; + +/** + * @type {import('semantic-release').GlobalConfig} + */ +module.exports = { + pkgRoot: srcRoot, + tagFormat: name + '/v${version}', + commitPaths: [`${srcRoot}/*`], + plugins: [ + ['@semantic-release/commit-analyzer', { + preset: 'angular', + releaseRules: [ + { type: 'refactor', release: 'patch' }, + ], + }], + ['@semantic-release/release-notes-generator', { + config: '@dialpad/conventional-changelog-angular', + }], + ['@dialpad/semantic-release-changelog-json', { + changelogFile: `${srcRoot}/CHANGELOG.md`, + changelogJsonFile: `${srcRoot}/CHANGELOG.json`, + }], + ['@semantic-release/changelog', { changelogFile: `${srcRoot}/CHANGELOG.md` }], + ['@semantic-release/npm', { npmPublish: false }], + ['@semantic-release/git', { + assets: [ + `${srcRoot}/CHANGELOG.md`, + `${srcRoot}/CHANGELOG.json`, + `${srcRoot}/package.json`, + `${srcRoot}/vscode-dialtone-` + '${nextRelease.version}.vsix', + ], + message: `chore(release): NO-JIRA ${name}` + + '/v${nextRelease.version}\n\n${nextRelease.notes}', + }], + ['@semantic-release/github', { + successComment: false, + failTitle: false, + }], + ], + branches: [ + 'staging', + 'next', + { + name: 'beta', + prerelease: true, + }, + { + name: 'alpha', + prerelease: true, + }, + ], +};