Skip to content

Commit

Permalink
feat: DLT-2192 lsp-hover-provider (#563)
Browse files Browse the repository at this point in the history
Signed-off-by: Julio Ortega <julio.ortega@dialpad.com>
  • Loading branch information
juliodialpad authored Nov 19, 2024
1 parent c29e044 commit 1bd875a
Show file tree
Hide file tree
Showing 17 changed files with 329 additions and 110 deletions.
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

0 comments on commit 1bd875a

Please sign in to comment.