Skip to content

Commit

Permalink
feat: add highlight
Browse files Browse the repository at this point in the history
  • Loading branch information
pomelo-nwu committed Aug 14, 2024
1 parent c2cc520 commit 2036120
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 140 deletions.
21 changes: 14 additions & 7 deletions packages/studio-driver/src/gremlin-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface Table {
class CypherDriver {
private driver: any;
private uri: string;
constructor(uri: string, username: string = '', password: string = '') {
constructor(uri: string, username: string = 'admin', password: string = 'password') {
try {
const authenticator = new gremlin.driver.auth.PlainTextSaslAuthenticator(username, password);
const client = new gremlin.driver.Client(uri, {
Expand Down Expand Up @@ -107,7 +107,11 @@ class CypherDriver {
await this.handleTableResult(tableResult, value);
} else {
mode = 'table';
if (typeof value === 'number' || typeof value === 'string' || value instanceof BigNumber) {
if (
typeof value === 'number' ||
typeof value === 'string'
// || value instanceof BigNumber
) {
// e.g. `g.V().count()`, `g.V().id()`, `g.V().label()`
tableResult.push(value);
} else {
Expand Down Expand Up @@ -139,15 +143,18 @@ class CypherDriver {
for (const edgeKey in edgeItemsMapping) {
edges.push(edgeItemsMapping[edgeKey]);
}
console.log({
return {
nodes,
edges,
mode,
tableResult,
});
table: tableResult,
raw: result,
};
}
if (mode === 'table') {
return {
nodes,
edges,
nodes: [],
edges: [],
mode,
table: tableResult,
raw: result,
Expand Down
240 changes: 113 additions & 127 deletions packages/studio-query/src/components/basic-languages-gremlin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ export const conf: languages.LanguageConfiguration = {
],
};

export const language = <languages.IMonarchLanguage>{
export const language: languages.IMonarchLanguage = {
defaultToken: '',
tokenPostfix: `.gremlin`,
tokenPostfix: '.gremlin',
ignoreCase: true,

brackets: [
Expand All @@ -42,129 +42,46 @@ export const language = <languages.IMonarchLanguage>{
keywords: [
'V',
'E',
'addV',
'addE',
'has',
'hasNot',
'out',
'in',
'both',
'outE',
'inE',
'bothE',
'outV',
'inV',
'bothV',
'has',
'hasLabel',
'limit',
'order',
'by',
'select',
'where',
'group',
'groupCount',
'count',
'sum',
'max',
'min',
'mean',
'and',
'or',
'not',
'emit',
'until',
'repeat',
'simplePath',
'timeLimit',
'tree',
'path',
'local',
'limit',
'range',
'skip',
'tail',
'union',
'fold',
'unfold',
'as',
'select',
'match',
'project',
'flatMap',
'choose',
'coalesce',
'where',
'filter',
'map',
'dedup',
'subgraph',
'valueMap',
'constant',
'inject',
'option',
'sideEffect',
'store',
'aggregate',
'order',
'coin',
'sample',
'emit',
'barrier',
'outE',
'inE',
'bothE',
],

builtinLiterals: ['true', 'false', 'null'],
builtinFunctions: [
'values',
'keys',
'property',
'valueMap',
'elementMap',
'label',
'id',
'identity',
'count',
'label',
'property',
'outV',
'inV',
'bothV',
'sum',
'max',
'min',
'mean',
'and',
'or',
'not',
'min',
'max',
'coalesce',
'constant',
'choose',
'filter',
'hasNext',
'next',
'toList',
'toSet',
'iterate',
'to',
'from',
'path',
'order',
'by',
'is',
'not',
'range',
'fold',
],

operators: [
// Math operators
'+',
'-',
'*',
'/',
'%',
'^',
// Comparison operators
'=',
'<>',
'<',
'>',
'<=',
'>=',
'==',
'!=',
// Traversal operators
'.',
'->',
'<-',
'-->',
'<--',
],
operators: ['+', '-', '*', '/', '%', '^', '=', '<>', '<', '>', '<=', '>='],

escapes: /\\(?:[tbnrf\\"'`]|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
digits: /\d+/,
Expand All @@ -178,39 +95,24 @@ export const language = <languages.IMonarchLanguage>{
{ include: '@numbers' },
{ include: '@strings' },

// Gremlin labels on vertices/edges, e.g. (v:Label)-[e:EdgeLabel]
[/:[a-zA-Z_][\w]*/, 'type.identifier'],

[
/[a-zA-Z_][\w]*(?=\()/,
{
cases: {
'@builtinFunctions': 'predefined.function',
},
},
],
[
/[a-zA-Z_$][\w$]*/,
{
cases: {
'@keywords': 'keyword',
'@builtinLiterals': 'predefined.literal',
'@default': 'identifier',
},
},
],

[/`/, 'identifier.escape', '@identifierBacktick'],

// delimiter and operator after number because of `.\d` floats and `:` in labels
[/[;,.:|]/, 'delimiter'],
[
/[<>=%+\-*/^]+/,
{
cases: {
'@operators': 'delimiter',
'@default': '',
},
},
],
[/[\+\-\*\/\^%]+/, 'delimiter.operator'],
[/[\!<>\?=\|&]/, 'delimiter'],
],
numbers: [
[/-?(@digits)[eE](-?(@digits))?/, 'number.float'],
Expand All @@ -220,8 +122,8 @@ export const language = <languages.IMonarchLanguage>{
[/-?(@digits)/, 'number'],
],
strings: [
[/"([^"\\]|\\.)*$/, 'string.invalid'], // non-terminated string
[/'([^'\\]|\\.)*$/, 'string.invalid'], // non-terminated string
[/"([^"\\]|\\.)*$/, 'string.invalid'],
[/'([^'\\]|\\.)*$/, 'string.invalid'],
[/"/, 'string', '@stringDouble'],
[/'/, 'string', '@stringSingle'],
],
Expand Down Expand Up @@ -256,3 +158,87 @@ export const language = <languages.IMonarchLanguage>{
],
},
};

export function registerGremlinLanguage(): void {
languages.register({ id: 'gremlin' });
languages.setMonarchTokensProvider('gremlin', language);
languages.setLanguageConfiguration('gremlin', conf);

languages.registerCompletionItemProvider('gremlin', {
triggerCharacters: ['.'],
provideCompletionItems: function (model, position) {
const lineContent = model.getLineContent(position.lineNumber);
const textBeforeCursor = lineContent.slice(0, position.column - 1).trim();

// 提取最后一个词
const lastWord = extractLastWord(textBeforeCursor);

// 根据最后一个词生成补全建议
const suggestions = generateSuggestionsForLastWord(lastWord);

return { suggestions: suggestions };
},
});
}

// 提取文本中的最后一个词
function extractLastWord(text) {
// 移除所有括号及其内容
const cleanText = text.replace(/\(.*?\)/g, '');
// 按空白字符和句点分割文本,过滤掉空白字符
const words = cleanText.split(/\s|\./).filter(Boolean);
return words.length > 0 ? words[words.length - 1] : '';
}

const project = ['id', 'label', 'constant', 'valueMap', 'values', 'elementMap', 'select'];

const aggregate = ['count', 'fold', 'sum', 'min', 'max', 'mean', 'group', 'groupCount'];

const filter = [
'hasId',
'hasLabel',
'has',
'hasNot',
'is',
'where',
'not',
'dedup',
'order',
'limit',
'coin',
'sample',
'union',
];
const common = [...aggregate, ...filter, ...project];

const vertexStep = ['outV', 'inV', 'otherV', 'bothV'];
const edgeStep = ['outE', 'inE', 'bothE', 'out', 'in', 'both'];

const StepMap = {};
[...vertexStep, 'out', 'in', 'both'].forEach(item => {
StepMap[item] = [...edgeStep, ...common];
});

['outE', 'inE', 'bothE'].forEach(item => {
StepMap[item] = [...vertexStep, ...common];
});

function generateSuggestionsForLastWord(lastWord) {
const contextSpecificSuggestions = {
g: ['V', 'E', 'match'],
V: [...edgeStep, ...common],
E: [...vertexStep, ...common],
...StepMap,
};

// 获取具体上下文的补全建议
const specificSuggestions = contextSpecificSuggestions[lastWord] || [];
return specificSuggestions.map(key => {
return {
label: key,
kind: languages.CompletionItemKind.Function,
documentation: key,
insertText: `${key}()`,
};
});
}
8 changes: 2 additions & 6 deletions packages/studio-query/src/components/cypher-editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@ import { useThemeContainer } from '@graphscope/studio-components';
import './index.css';
import { editor, languages } from 'monaco-editor';
import 'monaco-editor/esm/vs/basic-languages/cypher/cypher.contribution';
import { language, conf } from '../basic-languages-gremlin/index';
import { registerGremlinLanguage } from '../basic-languages-gremlin/index';

// 注册 Gremlin 语言
languages.register({ id: 'gremlin' });
// 注册语法高亮
languages.setMonarchTokensProvider('gremlin', language);
// 注册配置
languages.setLanguageConfiguration('gremlin', conf);
registerGremlinLanguage();

function countLines(str) {
// 使用正则表达式匹配换行符,并计算匹配到的数量,即为行数
Expand Down

0 comments on commit 2036120

Please sign in to comment.