Skip to content

Commit

Permalink
feat: snippets demo
Browse files Browse the repository at this point in the history
  • Loading branch information
jialan committed Oct 21, 2024
1 parent 3e78633 commit 3a7c7c1
Show file tree
Hide file tree
Showing 13 changed files with 508 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
.history
/docs/
.DS_Store
*.tgz
#*.tgz
Binary file added dt-sql-parser-4.0.2.tgz
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
}
},
"dependencies": {
"dt-sql-parser": "4.0.2"
"dt-sql-parser": "file:./dt-sql-parser-4.0.2.tgz"
},
"peerDependencies": {
"monaco-editor": ">=0.31.0"
Expand Down
25 changes: 14 additions & 11 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 47 additions & 3 deletions src/baseSQLWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ export interface ICreateData {
languageId: string;
}

export type SnippetToken = {
line: number;
column: number;
text: string | null;
start: number;
stop: number;
type: number;
isWhiteSpace: boolean;
isIdentify: boolean;
isStringLiteral: boolean;
};

export abstract class BaseSQLWorker {
protected abstract _ctx: worker.IWorkerContext;
protected abstract parser: BasicSQL;
Expand Down Expand Up @@ -45,17 +57,49 @@ export abstract class BaseSQLWorker {
async doCompletionWithEntities(
code: string,
position: Position
): Promise<[Suggestions | null, EntityContext[] | null]> {
): Promise<{
suggestions: Suggestions | null;
allEntities: EntityContext[] | null;
context?: any;
}> {
code = code || this.getTextDocument();
if (code) {
const suggestions = this.parser.getSuggestionAtCaretPosition(code, position);
let allEntities = null;
if (suggestions?.syntax?.length) {
allEntities = this.parser.getAllEntities(code, position);
}
return Promise.resolve([suggestions, allEntities]);
const contextType = this.parser.getContextTypeAtCaretPosition(code, position);

return Promise.resolve({
suggestions,
allEntities,
context: contextType
});
}
return Promise.resolve([null, null]);

return Promise.resolve({
suggestions: null,
allEntities: null,
context: null
});
}

async getAllTokens(code: string): Promise<SnippetToken[]> {
code = code || this.getTextDocument();
const tokens: SnippetToken[] = this.parser.getAllTokens(code).map((item) => ({
line: item.line,
column: item.column,
text: item.text,
start: item.start,
stop: item.stop,
type: item.type,
// todo replace
isWhiteSpace: item.type === 434,
isIdentify: item.type === 432,
isStringLiteral: item.type === 426
}));
return Promise.resolve(tokens);
}

async getAllEntities(code: string, position?: Position): Promise<EntityContext[] | null> {
Expand Down
90 changes: 86 additions & 4 deletions src/languageFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {
CancellationToken
} from './fillers/monaco-editor-core';
import { debounce } from './common/utils';
import { BaseSQLWorker } from './baseSQLWorker';
import { BaseSQLWorker, SnippetToken } from './baseSQLWorker';
import type { ParseError } from 'dt-sql-parser';
import type { LanguageServiceDefaults } from './monaco.contribution';
import type { CompletionSnippet, LanguageServiceDefaults } from './monaco.contribution';

export interface WorkerAccessor<T extends BaseSQLWorker> {
(...uris: Uri[]): Promise<T>;
Expand Down Expand Up @@ -130,6 +130,25 @@ function toDiagnostics(_resource: Uri, diag: ParseError): editor.IMarkerData {
};
}

function mininumCode(model: editor.IModel, position: Position) {
const codeBeforePosition = model.getValueInRange(
new Range(0, 0, position.lineNumber, position.column)
);
const separatorIndex = codeBeforePosition.lastIndexOf(';');
return separatorIndex === -1
? codeBeforePosition
: codeBeforePosition.slice(separatorIndex + 1);
}

// async function getSQLSnippets(languageId: string) {
// switch (languageId) {
// case 'hivesql':
// return import('./languages/hive/hive.snippet');
// default:
// return Promise.resolve({snippets: []})
// }
// }

export class CompletionAdapter<T extends BaseSQLWorker>
implements languages.CompletionItemProvider
{
Expand Down Expand Up @@ -159,13 +178,22 @@ export class CompletionAdapter<T extends BaseSQLWorker>
}
return worker.doCompletionWithEntities(code, position);
})
.then(([suggestions, allEntities]) => {
.then(async ({ suggestions, allEntities, context: sqlContext }) => {
let snippets: CompletionSnippet[] = [];
if (sqlContext?.newStatement) {
snippets = this._defaults.completionSnippets.map((item) => ({
...item,
insertText: typeof item.body === 'string' ? item.body : item.body.join('\n')
}));
}

return this._defaults.completionService(
model,
position,
context,
suggestions,
allEntities
allEntities,
snippets
);
})
.then((completions) => {
Expand Down Expand Up @@ -197,3 +225,57 @@ export class CompletionAdapter<T extends BaseSQLWorker>
});
}
}

export class InlineCompletionAdapter<T extends BaseSQLWorker>
implements languages.InlineCompletionsProvider
{
constructor(
private readonly _worker: WorkerAccessor<T>,
private readonly _defaults: LanguageServiceDefaults
) {
console.log('模板列表', _defaults.inlineCompletionSnippets);
}

private allSnippetsTokens: { tokens: SnippetToken[]; snippetText: string }[] = [];

provideInlineCompletions(
model: editor.ITextModel,
position: Position
): languages.ProviderResult<languages.InlineCompletions<languages.InlineCompletion>> {
const resource = model.uri;
return this._worker(resource)
.then(async (worker) => {
let code = mininumCode(model, position);
if (typeof this._defaults.preprocessCode === 'function') {
code = this._defaults.preprocessCode(code);
}

if (!this.allSnippetsTokens?.length) {
const allSnippetsTokens = [];
for (const snippet of this._defaults.inlineCompletionSnippets) {
const tokens = await worker.getAllTokens(snippet);
allSnippetsTokens.push({ tokens, snippetText: snippet });
}
this.allSnippetsTokens = allSnippetsTokens;
}

const currentCodeTokens = await worker.getAllTokens(code);

return {
code,
codeTokens: currentCodeTokens,
allSnippetsTokens: this.allSnippetsTokens
};
})
.then(({ code, codeTokens, allSnippetsTokens }) => {
return this._defaults.inlineCompletionService(
model,
position,
code,
codeTokens,
allSnippetsTokens
) as any;
});
}
freeInlineCompletions(_completions: any) {}
}
12 changes: 12 additions & 0 deletions src/languages/hive/hive.snippet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const snippets = [
{
prefix: 'INSERT',
label: 'INSERT 默认模板',
body: ['insert', 'into', ' `${1:table1}`', 'values', ' (`$2`);', '${3}']
},
{
prefix: 'SELECT',
label: 'SELECT 默认模板',
body: ['select', ' ${1:id}', 'from', ' ${2:table1};', '${3}']
}
];
50 changes: 49 additions & 1 deletion src/monaco.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,19 @@ export type CompletionService = (
position: Position,
completionContext: languages.CompletionContext,
suggestions: Suggestions | null,
entities: EntityContext[] | null
entities: EntityContext[] | null,
snippets?: CompletionSnippet[],
identifiers?: any[]
) => Promise<ICompletionItem[] | ICompletionList>;

export type InlineCompletionService = (
model: editor.IReadOnlyModel,
position: Position,
code: string,
tokens: any[],
allSnippetsTokens: any[]
) => Promise<languages.InlineCompletions<languages.InlineCompletion> | null | undefined>;

export interface CompletionOptions {
enable: boolean;
/**
Expand All @@ -42,6 +52,21 @@ export interface CompletionOptions {
*/
completionService: CompletionService;
triggerCharacters: string[];
snippets: CompletionSnippet[];
}

export interface InlineCompletionOptions {
enable: boolean;
completionService: InlineCompletionService;
inlineSnippets: string[];
}

export interface CompletionSnippet {
prefix: string;
label: string;
body: string | string[];
// generated by body
insertText?: string;
}

export interface ModeConfiguration {
Expand All @@ -51,6 +76,8 @@ export interface ModeConfiguration {
*/
readonly completionItems: CompletionOptions;

readonly inlineCompletionItems: any;

/**
* Defines whether the built-in diagnostic provider is enabled.
*/
Expand Down Expand Up @@ -90,6 +117,9 @@ export interface LanguageServiceDefaults {
readonly modeConfiguration: ModeConfiguration;
preprocessCode: PreprocessCode | null;
completionService: CompletionService;
inlineCompletionService: any;
completionSnippets: CompletionSnippet[];
inlineCompletionSnippets: string[];
triggerCharacters: string[];
setModeConfiguration(modeConfiguration: ModeConfiguration): void;
}
Expand Down Expand Up @@ -126,6 +156,18 @@ export class LanguageServiceDefaultsImpl implements LanguageServiceDefaults {
return this._modeConfiguration.completionItems.completionService;
}

get inlineCompletionService(): any {
return this._modeConfiguration.inlineCompletionItems.completionService;
}

get completionSnippets(): CompletionSnippet[] {
return this._modeConfiguration.completionItems.snippets;
}

get inlineCompletionSnippets(): string[] {
return this._modeConfiguration.inlineCompletionItems.inlineSnippets;
}

get triggerCharacters(): string[] {
return this._modeConfiguration.completionItems.triggerCharacters;
}
Expand Down Expand Up @@ -167,6 +209,12 @@ export const defaultCompletionService: CompletionService = function (

export const modeConfigurationDefault: Required<ModeConfiguration> = {
completionItems: {
enable: true,
completionService: defaultCompletionService,
triggerCharacters: ['.', ' '],
snippets: []
},
inlineCompletionItems: {
enable: true,
completionService: defaultCompletionService,
triggerCharacters: ['.', ' ']
Expand Down
Loading

0 comments on commit 3a7c7c1

Please sign in to comment.