mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Implement AI Assistant UI V2
Summary: Implements the latest design of the Formula AI Assistant. Also switches out brace to the latest build of ace. Test Plan: Browser tests. Reviewers: jarek Reviewed By: jarek Subscribers: jarek Differential Revision: https://phab.getgrist.com/D3949
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
.ace_editor {
|
||||
background-color: var(--grist-theme-ace-editor-bg, white);
|
||||
}
|
||||
|
||||
.ace_grist_link_hidden {
|
||||
display: none;
|
||||
}
|
||||
@@ -14,6 +18,7 @@
|
||||
|
||||
.ace_editor.ace_autocomplete .ace_completion-highlight {
|
||||
color: var(--grist-theme-ace-autocomplete-highlighted-fg, #000) !important;
|
||||
text-shadow: 0 0 0.01em;
|
||||
}
|
||||
|
||||
.ace_editor.ace_autocomplete .ace_completion-highlight.ace_grist_link {
|
||||
@@ -41,3 +46,8 @@
|
||||
.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {
|
||||
background-color: var(--grist-theme-ace-autocomplete-active-line-bg, #CAD6FA) !important;
|
||||
}
|
||||
|
||||
.ace_autocomplete .ace_line .ace_ {
|
||||
/* Ace collapses whitespace by default, which breaks alignment changes made in AceEditorCompletions.ts. */
|
||||
white-space: pre !important;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
var ace = require('brace');
|
||||
var ace = require('ace-builds');
|
||||
var _ = require('underscore');
|
||||
// Used to load python language settings and color themes
|
||||
require('brace/mode/python');
|
||||
require('brace/theme/chrome');
|
||||
require('brace/theme/dracula');
|
||||
require('brace/ext/language_tools');
|
||||
// ace-builds also has a minified build (src-min-noconflict), but we don't
|
||||
// use it since webpack already handles minification.
|
||||
require('ace-builds/src-noconflict/mode-python');
|
||||
require('ace-builds/src-noconflict/theme-chrome');
|
||||
require('ace-builds/src-noconflict/theme-dracula');
|
||||
require('ace-builds/src-noconflict/ext-language_tools');
|
||||
var {setupAceEditorCompletions} = require('./AceEditorCompletions');
|
||||
var {getGristConfig} = require('../../common/urlUtils');
|
||||
var dom = require('../lib/dom');
|
||||
@@ -291,7 +292,7 @@ AceEditor.prototype._setAceTheme = function(gristTheme) {
|
||||
|
||||
let _RangeConstructor = null; //singleton, load it lazily
|
||||
AceEditor.makeRange = function(a, b, c, d) {
|
||||
_RangeConstructor = _RangeConstructor || ace.acequire('ace/range').Range;
|
||||
_RangeConstructor = _RangeConstructor || ace.require('ace/range').Range;
|
||||
return new _RangeConstructor(a, b, c, d);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import ace, {Ace} from 'ace-builds';
|
||||
import {ISuggestionWithValue} from 'app/common/ActiveDocAPI';
|
||||
import {commonUrls} from 'app/common/gristUrls';
|
||||
import * as ace from 'brace';
|
||||
|
||||
export interface ICompletionOptions {
|
||||
getSuggestions(prefix: string): Promise<ISuggestionWithValue[]>;
|
||||
}
|
||||
|
||||
const completionOptions = new WeakMap<ace.Editor, ICompletionOptions>();
|
||||
const completionOptions = new WeakMap<Ace.Editor, ICompletionOptions>();
|
||||
|
||||
export function setupAceEditorCompletions(editor: ace.Editor, options: ICompletionOptions) {
|
||||
export function setupAceEditorCompletions(editor: Ace.Editor, options: ICompletionOptions) {
|
||||
initCustomCompleter();
|
||||
completionOptions.set(editor, options);
|
||||
|
||||
@@ -17,7 +17,7 @@ export function setupAceEditorCompletions(editor: ace.Editor, options: ICompleti
|
||||
// It is important for autoSelect to be off so that hitting enter doesn't automatically
|
||||
// use a suggestion, a change of behavior that doesn't seem particularly desirable and
|
||||
// which also breaks several existing tests.
|
||||
const {Autocomplete} = ace.acequire('ace/autocomplete'); // lives in brace/ext/language_tools
|
||||
const {Autocomplete} = ace.require('ace/autocomplete');
|
||||
|
||||
const completer = new Autocomplete();
|
||||
completer.autoSelect = false;
|
||||
@@ -69,7 +69,7 @@ export function setupAceEditorCompletions(editor: ace.Editor, options: ICompleti
|
||||
// it adds to body even when it detaches itself. Ace's AutoCompleter doesn't expose any
|
||||
// interface for this, so this takes some hacking. (One reason for this is that Ace seems to
|
||||
// expect that a single AutoCompleter would be used for all editor instances.)
|
||||
editor.on('destroy', () => {
|
||||
editor.on('destroy' as any, () => {
|
||||
if (completer.editor) {
|
||||
completer.detach();
|
||||
}
|
||||
@@ -91,10 +91,10 @@ function initCustomCompleter() {
|
||||
const prefixMatchRegex = /\w+\.(?:lookupRecords|lookupOne)\([\w.$\u00A2-\uFFFF]*$|[\w.$\u00A2-\uFFFF]+$/;
|
||||
|
||||
// Monkey-patch getCompletionPrefix. This is based on the source code in
|
||||
// node_modules/brace/ext/language_tools.js, simplified to do the one thing we want here (since
|
||||
// the original method's generality doesn't help us here).
|
||||
const util = ace.acequire('ace/autocomplete/util'); // lives in brace/ext/language_tools
|
||||
util.getCompletionPrefix = function getCompletionPrefix(this: any, editor: ace.Editor) {
|
||||
// node_modules/ace-builds/src-noconflict/ext-language_tools.js, simplified to do the one thing
|
||||
// we want here (since the original method's generality doesn't help us here).
|
||||
const util = ace.require('ace/autocomplete/util');
|
||||
util.getCompletionPrefix = function getCompletionPrefix(this: any, editor: Ace.Editor) {
|
||||
const pos = editor.getCursorPosition();
|
||||
const line = editor.session.getLine(pos.row);
|
||||
const match = line.slice(0, pos.column).match(prefixMatchRegex);
|
||||
@@ -102,14 +102,14 @@ function initCustomCompleter() {
|
||||
};
|
||||
|
||||
// Add some autocompletion with partial access to document
|
||||
const aceLanguageTools = ace.acequire('ace/ext/language_tools');
|
||||
const aceLanguageTools = ace.require('ace/ext/language_tools');
|
||||
aceLanguageTools.setCompleters([]);
|
||||
aceLanguageTools.addCompleter({
|
||||
// For autocompletion we ship text to the sandbox and run standard completion there.
|
||||
async getCompletions(
|
||||
editor: ace.Editor,
|
||||
session: ace.IEditSession,
|
||||
pos: ace.Position,
|
||||
editor: Ace.Editor,
|
||||
session: Ace.EditSession,
|
||||
pos: Ace.Position,
|
||||
prefix: string,
|
||||
callback: any
|
||||
) {
|
||||
@@ -120,12 +120,13 @@ function initCustomCompleter() {
|
||||
// in the case where one function is being switched with another. Since we normally
|
||||
// append a "(" when completing such suggestions, we need to be careful not to do
|
||||
// so if a "(" is already present. One way to do this in ACE is to check if the
|
||||
// current token is an identifier, and the next token is a lparen; if both are true,
|
||||
// we skip appending a "(" to each suggestion.
|
||||
// current token is a function/identifier, and the next token is a lparen; if both are
|
||||
// true, we skip appending a "(" to each suggestion.
|
||||
const wordRange = session.getWordRange(pos.row, pos.column);
|
||||
const token = session.getTokenAt(pos.row, wordRange.end.column) as TokenInfo;
|
||||
const nextToken = session.getTokenAt(pos.row, wordRange.end.column + 1) as TokenInfo|null;
|
||||
const isRenamingFunc = token.type === 'identifier' && nextToken?.type === 'paren.lparen';
|
||||
const token = session.getTokenAt(pos.row, wordRange.end.column) as Ace.Token;
|
||||
const nextToken = session.getTokenAt(pos.row, wordRange.end.column + 1);
|
||||
const isRenamingFunc = ['function.support', 'identifier'].includes(token.type)
|
||||
&& nextToken?.type === 'paren.lparen';
|
||||
|
||||
const suggestions = await options.getSuggestions(prefix);
|
||||
// ACE autocompletions are very poorly documented. This is somewhat helpful:
|
||||
@@ -209,7 +210,8 @@ interface AceSuggestion {
|
||||
* them to look like links, and handle clicks to open the destination URL.
|
||||
*
|
||||
* This implementation relies a lot on the details of the implementation in
|
||||
* node_modules/brace/ext/language_tools.js. Updates to brace module may easily break it.
|
||||
* node_modules/ace-builds/src-noconflict/ext-language_tools.js. Updates to ace-builds module may
|
||||
* easily break it.
|
||||
*/
|
||||
function aceCompleterAddHelpLinks(completer: any) {
|
||||
// Replace the $init function in order to intercept the creation of the autocomplete popup.
|
||||
@@ -239,11 +241,7 @@ function customizeAceCompleterPopup(completer: any, popup: any) {
|
||||
});
|
||||
}
|
||||
|
||||
interface TokenInfo extends ace.TokenInfo {
|
||||
type: string;
|
||||
}
|
||||
|
||||
function retokenizeAceCompleterRow(rowData: AceSuggestion, tokens: TokenInfo[]): TokenInfo[] {
|
||||
function retokenizeAceCompleterRow(rowData: AceSuggestion, tokens: Ace.Token[]): Ace.Token[] {
|
||||
if (!(rowData.funcname || rowData.example)) {
|
||||
// Not a special completion, pass through the result of ACE's original tokenizing.
|
||||
return tokens;
|
||||
|
||||
Reference in New Issue
Block a user