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

feat: DLT-2192 lsp-hover-provider #563

Merged
merged 4 commits into from
Nov 19, 2024
Merged
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
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/*
5 changes: 5 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ on:
- css
- emojis
- icons
- language-server
- tokens
- vue2
- vue3
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,5 @@ packages/postcss-responsive-variations/coverage
# Language Server
*.tsbuildinfo
*.vsix
packages/language-server/sample/*.vue
packages/language-server/sample/*.css
12 changes: 8 additions & 4 deletions packages/language-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<dt-|` to try Component completion.
- Type `<dt-avatar | />` to try property completion.
- Have `<dt-avatar size="|" />` to see values completion.
- The [Extension Development Host] instance of VSCode, will open the `sample` folder.
- On a `.vue` file:
- Type `<dt-|` to trigger Component completion.
- Type `<dt-avatar | />` to trigger property completion.
- Type `<dt-avatar size="|" />` 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

Expand Down
12 changes: 5 additions & 7 deletions packages/language-server/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand All @@ -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
}
}
Expand Down
11 changes: 11 additions & 0 deletions packages/language-server/sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Create or open a `.vue` or a `.css` file to start testing the extension.

- On a `.vue` file:
- Type `<dt-|` to trigger Component completion.
- Type `<dt-avatar | />` to trigger property completion.
- Type `<dt-avatar size="|" />` 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.
10 changes: 0 additions & 10 deletions packages/language-server/sample/test.vue

This file was deleted.

3 changes: 0 additions & 3 deletions packages/language-server/sample/test2.css

This file was deleted.

54 changes: 54 additions & 0 deletions packages/language-server/server/release-ci.config.cjs
Original file line number Diff line number Diff line change
@@ -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,
},
],
};
2 changes: 1 addition & 1 deletion packages/language-server/server/src/language-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class DialtoneVirtualCode implements VirtualCode {
lengths: [this.snapshot.getLength()],
data: {
completion: true,
// semantic: true,
semantic: true,
},
}];
}
Expand Down
12 changes: 5 additions & 7 deletions packages/language-server/server/src/resolvers/css-variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = [];

Expand All @@ -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`;

Expand All @@ -63,8 +63,6 @@ function processDocumentation(docs: DialtoneTokensDoc) {
value: documentation,
} as MarkupContent;

variable.detail = variable.detail || 'Missing variable description';

variablesDocumentation.push(variable)
}

Expand All @@ -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<CompletionList> {
console.log('Resolving CSS Variables', currentWord);
Expand Down
64 changes: 32 additions & 32 deletions packages/language-server/server/src/resolvers/vue-components.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { CompletionContext, CompletionItem, CompletionList, NullableProviderResult } from "@volar/language-server/node";
import type { CompletionItem, CompletionList, NullableProviderResult } from "@volar/language-server/node";
import { CompletionItemKind } from "@volar/language-server/node";
import { stringToHumanReadable, stringToKebabCase } from "../utils";

Expand All @@ -25,7 +25,7 @@ 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);
Expand All @@ -35,53 +35,53 @@ export const components = componentDocumentation.map((component: DialtoneCompone
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<CompletionList> {
console.log('Resolving Vue Components', currentLine, currentWord, sanitizedWord, context);

// Get the clean tag-name
const tagName = currentLine.replace(/\s+<([\w-]+).*/, '$1');

export function resolveComponentProps(tagName: string, currentWord: string): NullableProviderResult<CompletionList> {
const component = componentDocumentation.find(component =>
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 (propValues?.length) {
return { isIncomplete: false, items: propValues }
}

const props = component.props
.filter(prop => stringToKebabCase(prop.name).startsWith(currentWord))
.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 =>
// @TODO: Filter properties that are already set
item.label.startsWith(sanitizedWord)
);
documentation: prop.description,
}) as CompletionItem);

console.info('Resolving props');
return { isIncomplete: true, items: props };
}

return { isIncomplete: false, items: props };
export function resolvePropValues(tagName: string, propName: string): NullableProviderResult<CompletionList> {
const component = componentDocumentation.find(component =>
stringToKebabCase(component.displayName) === tagName
);

if (!component)
return;

const prop = component.props.find(prop => stringToKebabCase(prop.name) === stringToKebabCase(propName));

if (!prop) return;

const propValues = prop.values?.map(val => ({
label: val,
kind: CompletionItemKind.Value,
}) as CompletionItem);

if (propValues?.length) {
console.info('Resolving values');
return { isIncomplete: false, items: propValues }
}
}
Loading
Loading