vscode#TextEdit TypeScript Examples

The following examples show how to use vscode#TextEdit. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: cfgFormat.ts    From sourcepawn-vscode with MIT License 6 votes vote down vote up
public provideDocumentFormattingEdits(
    document: TextDocument,
    options: FormattingOptions,
    token: CancellationToken
  ): ProviderResult<TextEdit[]> {
    let workspaceFolder = Workspace.getWorkspaceFolder(document.uri);

    // Get the user's settings.
    let insertSpaces: boolean =
      Workspace.getConfiguration("editor", workspaceFolder).get(
        "insertSpaces"
      ) || false;
    let tabSize: number =
      Workspace.getConfiguration("editor", workspaceFolder).get("tabSize") || 2;

    // Apply user settings
    const start = new Position(0, 0);
    const end = new Position(
      document.lineCount - 1,
      document.lineAt(document.lineCount - 1).text.length
    );
    let range = new Range(start, end);

    let text = formatCFGText(document.getText(), insertSpaces, tabSize);

    // If process failed,
    if (text === "") {
      window.showErrorMessage(
        "The formatter failed to run, check the console for more details."
      );
      return undefined;
    }
    return [new TextEdit(range, text)];
  }
Example #2
Source File: extension.ts    From format-imports-vscode with MIT License 6 votes vote down vote up
function sortImportsBeforeSavingDocument(event: TextDocumentWillSaveEvent) {
  const { document } = event;
  const format = async () => {
    const newSourceText = await formatDocument(document, 'onSave');
    if (newSourceText === undefined) return [];
    return [TextEdit.replace(fullRange(document), newSourceText)];
  };
  event.waitUntil(format());
}
Example #3
Source File: extension.ts    From vscode-crestron-splus with GNU General Public License v3.0 6 votes vote down vote up
public async provideDocumentRangeFormattingEdits(
        document: TextDocument,
        range: Range,
        _options: FormattingOptions,
        _token: CancellationToken
    ): Promise<TextEdit[]> {
        return this.provideEdits(document, {
            rangeEnd: document.offsetAt(range.end),
            rangeStart: document.offsetAt(range.start),
        });
    }
Example #4
Source File: quickfix-select-valid-type.ts    From al-objid with MIT License 6 votes vote down vote up
export async function quickFixSelectValidType(document: TextDocument, range: Range, remainingTypes: string[]) {
    const selectedType = await window.showQuickPick(remainingTypes, {
        placeHolder: "Select an object type...",
    });
    if (!selectedType) {
        return;
    }

    let replace = new WorkspaceEdit();
    replace.set(document.uri, [TextEdit.replace(range, `"${selectedType}"`)]);
    await workspace.applyEdit(replace);
    await document.save();
}
Example #5
Source File: spFormat.ts    From sourcepawn-vscode with MIT License 5 votes vote down vote up
public provideDocumentFormattingEdits(
    document: TextDocument,
    options: FormattingOptions,
    token: CancellationToken
  ): ProviderResult<TextEdit[]> {
    // Get the user's settings.
    let insertSpaces: boolean =
      Workspace.getConfiguration("editor").get("insertSpaces") || false;
    let UseTab: string = insertSpaces ? "Never" : "Always";
    let tabSize: number =
      Workspace.getConfiguration("editor").get("tabSize") || 2;

    let workspaceFolder = Workspace.getWorkspaceFolder(document.uri);
    let defaultStyles: string[] =
      Workspace.getConfiguration("sourcepawn", workspaceFolder).get(
        "formatterSettings"
      ) || [];

    let default_style: string = "{" + defaultStyles.join(", ") + "}";

    // Apply user settings
    default_style = default_style
      .replace(/\${TabSize}/g, tabSize.toString())
      .replace(/\${UseTab}/g, UseTab);
    const start = new Position(0, 0);
    const end = new Position(
      document.lineCount - 1,
      document.lineAt(document.lineCount - 1).text.length
    );
    const range = new Range(start, end);
    const tempFile = join(__dirname, "temp_format.sp");
    let file = openSync(tempFile, "w", 0o765);
    writeSync(file, document.getText());
    closeSync(file);
    let text = this.clangFormat(tempFile, "utf-8", default_style);

    // If process failed,
    if (text === undefined) {
      window.showErrorMessage(
        "The formatter failed to run, check the console for more details."
      );
      return undefined;
    }
    text = fixFormatting(text);
    return [new TextEdit(range, text)];
  }
Example #6
Source File: WorkspaceWatcher.ts    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
/**
   * When saving a note, do some book keeping
   * - update the `updated` time in frontmatter
   * - update the note metadata in the engine
   * @param event
   * @returns
   */
  private onWillSaveNote(event: TextDocumentWillSaveEvent) {
    const ctx = "WorkspaceWatcher:onWillSaveNote";
    const uri = event.document.uri;
    const engine = this._extension.getEngine();
    const fname = path.basename(uri.fsPath, ".md");
    const now = Time.now().toMillis();

    const note = NoteUtils.getNoteByFnameFromEngine({
      fname,
      vault: this._extension.wsUtils.getVaultFromUri(uri),
      engine,
    });

    // If we can't find the note, don't do anything
    if (!note) {
      // Log at info level and not error level for now to reduce Sentry noise
      Logger.info({
        ctx,
        msg: `Note with fname ${fname} not found in engine! Skipping updated field FM modification.`,
      });
      return;
    }

    // Return undefined if document is missing frontmatter
    if (!TextDocumentService.containsFrontmatter(event.document)) {
      return;
    }

    const content = event.document.getText();
    const match = NoteUtils.RE_FM_UPDATED.exec(content);
    let changes: TextEdit[] = [];

    // update the `updated` time in frontmatter if it exists and content has changed
    if (match && WorkspaceUtils.noteContentChanged({ content, note })) {
      Logger.info({ ctx, match, msg: "update activeText editor" });
      const startPos = event.document.positionAt(match.index);
      const endPos = event.document.positionAt(match.index + match[0].length);
      changes = [
        TextEdit.replace(new Range(startPos, endPos), `updated: ${now}`),
      ];

      // update the note in engine
      // eslint-disable-next-line  no-async-promise-executor
      const p = new Promise(async (resolve) => {
        note.updated = now;
        await engine.updateNote(note);
        return resolve(changes);
      });
      event.waitUntil(p);
    }
    return { changes };
  }
Example #7
Source File: command.ts    From typescript-explicit-types with GNU General Public License v3.0 5 votes vote down vote up
function executeFormatDocumentProvider(uri: Uri) {
  return commands.executeCommand<TextEdit[]>('vscode.executeFormatDocumentProvider', uri);
}
Example #8
Source File: extension.ts    From vscode-crestron-splus with GNU General Public License v3.0 5 votes vote down vote up
public async provideDocumentFormattingEdits(
        document: TextDocument,
        _options: FormattingOptions,
        _token: CancellationToken
    ): Promise<TextEdit[]> {
        return this.provideEdits(document);
    }
Example #9
Source File: extension.ts    From vscode-crestron-splus with GNU General Public License v3.0 5 votes vote down vote up
constructor(
        private provideEdits: (
            document: TextDocument,
            options?: RangeFormattingOptions
        ) => Promise<TextEdit[]>
    ) { }
Example #10
Source File: extension.ts    From vscode-crestron-splus with GNU General Public License v3.0 5 votes vote down vote up
async function formatProvider(document: TextDocument, _options?: RangeFormattingOptions): Promise<TextEdit[]> {
    let outputText = formatText(document.getText());
    return [new TextEdit(
        fullDocumentRange(document),
        outputText)];
}
Example #11
Source File: NextObjectIdCompletionItem.ts    From al-objid with MIT License 5 votes vote down vote up
getCompletionCommand(position: Position, uri: Uri, type: string, app: ALApp, objectId: NextObjectIdInfo): Command {
        return {
            command: NinjaCommand.CommitSuggestion,
            title: "",
            arguments: [
                async () => {
                    //TODO Assigning from public range (1..50000) for enum values results in "No more objects..." error
                    output.log(`Committing object ID auto-complete for ${type} ${objectId.id}`, LogLevel.Info);
                    const realId = await Backend.getNextNo(
                        app,
                        type,
                        app.manifest.idRanges,
                        true,
                        objectId.id as number
                    );
                    const notChanged = !realId || this.isIdEqual(realId.id, objectId.id as number);
                    Telemetry.instance.log("getNextNo-commit", app.hash, notChanged ? undefined : "different");
                    if (notChanged) {
                        return;
                    }
                    output.log(
                        `Another user has consumed ${type} ${objectId.id} in the meantime. Retrieved new: ${type} ${realId.id}`,
                        LogLevel.Info
                    );

                    let replacement = `${realId.id}`;
                    if (!realId.available) {
                        if (this._range) {
                            UI.nextId.showNoMoreInLogicalRangeWarning(this._range.description).then(result => {
                                if (result === LABELS.BUTTON_LEARN_MORE) {
                                    showDocument(DOCUMENTS.LOGICAL_RANGES);
                                }
                            });
                        } else {
                            syncIfChosen(app, UI.nextId.showNoMoreNumbersWarning());
                        }
                        replacement = "";
                    }

                    let replace = new WorkspaceEdit();
                    replace.set(uri, [
                        TextEdit.replace(
                            new Range(position, position.translate(0, objectId.id.toString().length)),
                            replacement
                        ),
                    ]);
                    workspace.applyEdit(replace);
                },
            ],
        };
    }
Example #12
Source File: completionProvider.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
export async function provideBlockCompletionItems(
  document: TextDocument,
  position: Position,
  token?: CancellationToken
): Promise<CompletionItem[] | undefined> {
  const ctx = "provideBlockCompletionItems";

  // No-op if we're not in a Dendron Workspace
  if (!DendronExtension.isActive()) {
    return;
  }

  let found: RegExpMatchArray | undefined;
  // This gets triggered when the user types ^, which won't necessarily happen inside a wikilink.
  // So check that the user is actually in a wikilink before we try.
  const line = document.lineAt(position.line);
  // There may be multiple wikilinks in this line
  const matches = line.text.matchAll(PARTIAL_WIKILINK_WITH_ANCHOR_REGEX);
  for (const match of matches) {
    if (_.isUndefined(match.groups) || _.isUndefined(match.index)) continue;
    const { entireLink } = match.groups;
    // If the current position is within this link, then we are trying to complete it
    if (
      match.index <= position.character &&
      position.character <= match.index + entireLink.length
    ) {
      found = match;
    }
  }
  if (
    _.isUndefined(found) ||
    _.isUndefined(found.index) ||
    _.isUndefined(found.groups) ||
    token?.isCancellationRequested
  )
    return;
  Logger.debug({ ctx, found });

  const timestampStart = process.hrtime();
  const engine = getDWorkspace().engine;

  let otherFile = false;
  let note: NoteProps | undefined;
  if (found.groups?.note) {
    // This anchor will be to another note, e.g. [[note#
    // `groups.note` may have vault name, so let's try to parse that
    const link = LinkUtils.parseLinkV2({ linkString: found.groups.note });
    const vault = link?.vaultName
      ? VaultUtils.getVaultByName({
          vaults: engine.vaults,
          vname: link?.vaultName,
        })
      : undefined;
    // If we couldn't find the linked note, don't do anything
    if (_.isNull(link) || _.isUndefined(link.value)) return;
    note = NoteUtils.getNotesByFnameFromEngine({
      fname: link.value,
      vault,
      engine,
    })[0];
    otherFile = true;
  } else {
    // This anchor is to the same file, e.g. [[#
    note = WSUtils.getNoteFromDocument(document);
  }

  if (_.isUndefined(note) || token?.isCancellationRequested) return;
  Logger.debug({ ctx, fname: note.fname });

  // If there is [[^ or [[^^ , remove that because it's not a valid wikilink
  const removeTrigger = isNotUndefined(found.groups.trigger)
    ? new TextEdit(
        new Range(
          position.line,
          found.index + 2,
          position.line,
          found.index + 2 + found.groups.trigger.length
        ),
        ""
      )
    : undefined;

  let filterByAnchorType: "header" | "block" | undefined;
  // When triggered by [[#^, only show existing block anchors
  let insertValueOnly = false;
  if (isNotUndefined(found.groups?.anchor)) {
    filterByAnchorType = "block";
    // There is already #^ which we are not removing, so don't duplicate it when inserting the text
    insertValueOnly = true;
  } else if (isNotUndefined(found.groups?.hash)) {
    filterByAnchorType = "header";
    // There is already # which we are not removing, so don't duplicate it when inserting the text
    insertValueOnly = true;
  }

  const blocks = await getExtension()
    .getEngine()
    .getNoteBlocks({ id: note.id, filterByAnchorType });
  if (
    _.isUndefined(blocks.data) ||
    blocks.error?.severity === ERROR_SEVERITY.FATAL
  ) {
    Logger.error({
      ctx,
      error: blocks.error || undefined,
      msg: `Unable to get blocks for autocomplete`,
    });
    return;
  }
  Logger.debug({ ctx, blockCount: blocks.data.length });

  // Calculate the replacement range. This must contain any text the user has typed for the block, but not the trigger symbols (#, ^, #^)
  // This is used to determine what the user has typed to narrow the options, and also to pick what will get replaced once the completion is picked.
  let start = found.index + 2; /* length of [[ */
  let end = start;
  if (found.groups.trigger) {
    // Skip the trigger ^
    start += found.groups.trigger.length;
    // Include the text user has typed after trigger
    end = start;
    if (found.groups.afterTrigger) end += found.groups.afterTrigger.length;
  }
  if (found.groups.beforeAnchor) {
    // Skip anchor # or #^
    start += found.groups.beforeAnchor.length;
    // Include the text user has typed after anchor
    end = start;
    if (found.groups.afterAnchor) end += found.groups.afterAnchor.length;
  }
  const range = new Range(position.line, start, position.line, end);
  Logger.debug({ ctx, start: range.start, end: range.end });

  const completions = blocks.data
    .map((block, index) => {
      const edits: TextEdit[] = [];
      if (removeTrigger) edits.push(removeTrigger);
      let anchor: DNoteAnchor | undefined = block.anchor;
      if (_.isUndefined(anchor)) {
        // We can't insert edits into other files, so we can't suggest blocks without existing anchors
        if (otherFile) return;
        anchor = {
          type: "block",
          // Using the "insecure" generator avoids blocking for entropy to become available. This slightly increases the
          // chance of conflicting IDs, but that's okay since we'll only insert one of these completions. (Could also put
          // the same id for all options, but it's unclear if VSCode might reuse these completions)
          value: genUUIDInsecure(),
        };
        const blockPosition = VSCodeUtils.point2VSCodePosition(
          block.position.end
        );
        edits.push(
          new TextEdit(
            new Range(blockPosition, blockPosition),
            // To represent a whole list, the anchor must be after the list with 1 empty line between
            block.type === DendronASTTypes.LIST
              ? `\n\n${AnchorUtils.anchor2string(anchor)}\n`
              : // To represent any other block, the anchor can be placed at the end of the block
                ` ${AnchorUtils.anchor2string(anchor)}`
          )
        );
      }
      return {
        label: block.text,
        // The region that will get replaced when inserting the block.
        range,
        insertText: insertValueOnly
          ? anchor.value
          : `#${AnchorUtils.anchor2string(anchor)}`,
        // If the block didn't have an anchor, we need to insert it ourselves
        additionalTextEdits: edits,
        sortText: padWithZero(index),
      };
    })
    .filter(isNotUndefined);
  const duration = getDurationMilliseconds(timestampStart);
  Logger.debug({ ctx, completionCount: completions.length, duration });
  return completions;
}
Example #13
Source File: client.ts    From vscode-q with MIT License 4 votes vote down vote up
export function activate(context: ExtensionContext): void {

    // extra language configurations
    languages.setLanguageConfiguration('q', {
        onEnterRules: [
            {
                // eslint-disable-next-line no-useless-escape
                beforeText: /^(?!\s+).*[\(\[{].*$/,
                afterText: /^[)}\]]/,
                action: {
                    indentAction: IndentAction.None,
                    appendText: '\n '
                }
            },
            {
                // eslint-disable-next-line no-useless-escape
                beforeText: /^\s[)}\]];?$/,
                action: {
                    indentAction: IndentAction.Outdent
                }
            },
            {
                // eslint-disable-next-line no-useless-escape
                beforeText: /^\/.*$/,
                action: {
                    indentAction: IndentAction.None,
                    appendText: '/ '
                }
            }
        ]
    });

    // append space to start [,(,{
    languages.registerDocumentFormattingEditProvider('q', {
        provideDocumentFormattingEdits(document: TextDocument): TextEdit[] {
            const textEdit: TextEdit[] = [];
            for (let i = 0; i < document.lineCount; i++) {
                const line = document.lineAt(i);
                if (line.isEmptyOrWhitespace) {
                    continue;
                }

                if (line.text.match('^[)\\]}]')) {
                    textEdit.push(TextEdit.insert(line.range.start, ' '));
                }
            }
            return textEdit;
        }
    });

    // <-- init
    QStatusBarManager.create(context);
    QStatusBarManager.updateConnStatus(undefined);
    // q-server-explorer
    const qServers = new QServerTree('root', null);
    const qRoot = new QDictTreeItem('root', null);
    const qHistory = HistoryTreeItem.createHistoryTree();
    window.registerTreeDataProvider('q-servers', qServers);
    qServers.refresh();
    window.registerTreeDataProvider('q-explorer', qRoot);
    window.registerTreeDataProvider('q-history', qHistory);
    qHistory.refresh();
    QueryConsole.createOrGet();
    QueryView.setExtensionPath(context.extensionPath);
    QueryGrid.setExtensionPath(context.extensionPath);
    AddServer.setExtensionPath(context.extensionPath);
    ChartView.setExtensionPath(context.extensionPath);
    // --> init


    // <-- configuration
    const queryMode = workspace.getConfiguration().get('q-client.queryMode');
    QConnManager.setQueryMode(queryMode as string);
    // -->

    commands.registerCommand(
        'q-client.refreshEntry', () => qServers.refresh());

    commands.registerCommand(
        'q-explorer.toggleAutoRefresh', () => {
            QConnManager.autoRefreshExplorer = !QConnManager.autoRefreshExplorer;
            window.showInformationMessage(`Auto Refresh Explorer ${QConnManager.autoRefreshExplorer ? 'On' : 'Off'}`);
        });

    // q cfg input
    commands.registerCommand(
        'q-client.addEntry',
        () => {
            AddServer.createOrShow();
        });

    commands.registerCommand(
        'q-client.editEntry',
        (qConn: QConn) => {
            AddServer.createOrShow();
            AddServer.update(qConn);
        });

    commands.registerCommand(
        'q-client.deleteEntry',
        (qConn: QConn) => {
            window.showInputBox(
                { prompt: `Confirm to Remove Server '${qConn.uniqLabel.replace(',', '-')}' (Y/n)` }
            ).then(value => {
                if (value === 'Y') {
                    QConnManager.current?.removeCfg(qConn.uniqLabel);

                }
            });
        });

    commands.registerCommand(
        'q-client.reactions',
        async () => {
            const option = await window.showQuickPick(
                ['1 - Raising an Issue', '2 - Writing a Review', '3 - Creating a Pull Request', '4 - Buying Me a Beer', '5 - Q & A'],
                { placeHolder: 'Contribute to vscode-q by' });
            switch (option?.[0]) {
                case '1':
                    env.openExternal(Uri.parse('https://github.com/jshinonome/vscode-q/issues'));
                    break;
                case '2':
                    env.openExternal(Uri.parse('https://marketplace.visualstudio.com/items?itemName=jshinonome.vscode-q&ssr=false#review-details'));
                    break;
                case '3':
                    env.openExternal(Uri.parse('https://github.com/jshinonome/vscode-q/blob/master/CONTRIBUTING.md'));
                    break;
                case '4':
                    env.openExternal(Uri.parse('https://www.buymeacoffee.com/jshinonome'));
                    break;
                case '5':
                    env.openExternal(Uri.parse('https://github.com/jshinonome/vscode-q/discussions'));
            }
        });

    commands.registerCommand(
        'q-client.connectEntry',
        async () => {
            const connManager = QConnManager.current;
            if (connManager) {
                const quickPickItems: QuickPickItem[] = connManager.qCfg.map(qcfg => {
                    return {
                        label: qcfg.uniqLabel,
                        description: `\`:${qcfg.host}:${qcfg.port}`
                    };
                });
                const recentlyUsedItems: QuickPickItem[] = Array.from(connManager.qConnPool.values())
                    .filter(qConn => !(qConn.conn == undefined))
                    .map(qConn => {
                        return {
                            label: qConn.uniqLabel,
                            description: `\`:${qConn.host}:${qConn.port}`
                        };
                    });
                const allItems: QuickPickItem[] = [
                    {
                        label: 'Connected',
                        kind: QuickPickItemKind.Separator
                    },
                    ...recentlyUsedItems,
                    {
                        label: 'Process',
                        kind: QuickPickItemKind.Separator
                    },
                    ...quickPickItems
                ];
                const item = await window.showQuickPick(
                    allItems,
                    { placeHolder: 'Connect to a q process' });
                if (item) {
                    commands.executeCommand('q-client.connect', item.label);
                    return item.label;
                }
            }
        });

    commands.registerCommand(
        'q-client.connect',
        uniqLabel => {
            QConnManager.current?.connect(uniqLabel);
        });

    commands.registerCommand(
        'q-client.tagEntry',
        async (qConn: QConn) => {
            qConn.tags = await window.showInputBox({
                prompt: `Tags for '${qConn.label}' separate by ',' (e.g. 'dev,quant,tca')`
            }) ?? '';
            QConnManager.current?.addCfg(qConn);
        });

    commands.registerCommand(
        'q-client.switchMode',
        async () => {
            const mode = await window.showQuickPick(['Console', 'Grid', 'Visualization'],
                { placeHolder: 'Please choose a query mode from the list below' });
            if (mode) {
                window.showInformationMessage(`Switch to Query ${mode} Mode`);
                QConnManager.setQueryMode(mode);
                QStatusBarManager.updateQueryModeStatus();
            }
        });

    commands.registerCommand(
        'q-client.toggleLimitQuery',
        () => {
            QConnManager.current?.toggleLimitQuery();
        });

    commands.registerCommand(
        'q-client.abortQuery',
        () => {
            QConnManager.current?.abortQuery();
        });

    commands.registerCommand(
        'q-client.exportServers',
        () => {
            QConnManager.current?.exportCfg();
        });

    commands.registerCommand(
        'q-client.importServers',
        () => {
            QConnManager.current?.importCfg();
        });


    commands.registerCommand(
        'q-explorer.refreshEntry', () => qRoot.refresh());

    const previewQueryLimit = workspace.getConfiguration().get('q-client.expl.prevQueryLimit');

    commands.registerCommand('q-explorer.preview', (item: TreeItem) => {
        switch (item.contextValue) {
            case 'qtable':
                QConnManager.current?.sync(
                    `{[t;l]$[@[{x in value \`.Q.pt};t;{'"preview feature requires v3.5+ kdb"}];select from t where date=last date, i<l;select from t where i<l]}[\`${item.label};${previewQueryLimit}]`);
                break;
            case 'qfunction':
                QueryConsole.current?.append((item as QFunctionTreeItem).getBody(), 0, 'cached');
                break;
            default:
                if (item.label)
                    QConnManager.current?.sync(item.label as string);
        }
    });

    commands.registerCommand('q-explorer.click', label => {
        console.log(label);
    });

    context.subscriptions.push(
        commands.registerCommand('q-history.rerun', (history) => {
            if (QConnManager.current?.activeConn?.uniqLabel === history.uniqLabel)
                QConnManager.current?.sync(history.query);
            else
                QConnManager.current?.connect(history.uniqLabel, history.query);
        })
    );

    enum QueryCodeType {
        Line,
        Selection,
        Block
    }

    enum QueryTarget {
        Process,
        Terminal
    }

    async function queryCode(code: QueryCodeType, target: QueryTarget): Promise<void> {
        const editor = window.activeTextEditor;
        if (editor) {
            let n = 0;
            let query = '';
            const params = {
                textDocument: {
                    uri: editor.document.uri.toString(),
                },
                position: editor.selection.active,
            };
            switch (code) {
                case QueryCodeType.Line:
                    n = editor.selection.active.line;
                    query = editor.document.lineAt(n).text;
                    n = 0;
                    break;
                case QueryCodeType.Selection:
                    query = editor?.document.getText(
                        new Range(editor.selection.start, editor.selection.end)
                    );
                    break;
                case QueryCodeType.Block:
                    ({ query, n } = await client.sendRequest('onQueryBlock', params));
                    break;
            }
            if (query) {
                if (code === QueryCodeType.Line && query.startsWith('/<=>')) {
                    const uniqLabel = query.substring(4).trim();
                    commands.executeCommand('q-client.connect', uniqLabel);
                } else {
                    switch (target) {
                        case QueryTarget.Process:
                            QConnManager.current?.sync(query);
                            break;
                        case QueryTarget.Terminal:
                            sendToCurrentTerm(query.replace(/(\r\n|\n|\r)/gm, ''));
                            break;
                    }
                }
            }
            if (n > 0) {
                let line = editor.document.lineAt(n).text;
                while (n < editor.document.lineCount && (line === '' || line.startsWith('/'))) {
                    n++;
                    line = editor.document.lineAt(n).text;
                }
                const range = editor.document.lineAt(n).range;
                editor.selection = new Selection(range.start, range.start);
                editor.revealRange(new Range(range.start, range.start));
            }
        }
    }

    context.subscriptions.push(
        commands.registerCommand('q-client.queryCurrentLine', async () => {
            queryCode(QueryCodeType.Line, QueryTarget.Process);
        })
    );

    context.subscriptions.push(
        commands.registerCommand('q-client.querySelection', () => {
            queryCode(QueryCodeType.Selection, QueryTarget.Process);
        })
    );

    context.subscriptions.push(
        commands.registerCommand('q-client.queryBlock', () => {
            queryCode(QueryCodeType.Block, QueryTarget.Process);
        })
    );

    context.subscriptions.push(
        commands.registerCommand('q-term.sendCurrentLine', () => {
            queryCode(QueryCodeType.Line, QueryTarget.Terminal);
        })
    );

    context.subscriptions.push(
        commands.registerCommand('q-term.sendSelection', () => {
            queryCode(QueryCodeType.Selection, QueryTarget.Terminal);
        })
    );

    context.subscriptions.push(
        commands.registerCommand('q-term.sendBlock', () => {
            queryCode(QueryCodeType.Block, QueryTarget.Terminal);
        })
    );

    context.subscriptions.push(
        commands.registerCommand('q-client.terminal.run', () => {
            const filepath = window.activeTextEditor?.document.fileName;
            if (filepath)
                runQFile(filepath);
        })
    );

    context.subscriptions.push(
        commands.registerCommand('q-client.insertActiveConnLabel', () => {
            const editor = window.activeTextEditor;
            if (editor) {
                editor.edit(editBuilder => {
                    if (QConnManager.current?.activeConn?.uniqLabel) {
                        editBuilder.insert(editor.selection.active, '/<=>' + QConnManager.current?.activeConn?.uniqLabel);
                    }
                });
            }
        })
    );

    if (window.registerWebviewPanelSerializer) {
        // Make sure we register a serializer in activation event
        window.registerWebviewPanelSerializer(QueryView.viewType, {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            async deserializeWebviewPanel(webviewPanel: WebviewPanel) {
                QueryView.revive(webviewPanel, context.extensionPath);
            }
        });
    }

    workspace.onDidChangeConfiguration(e => {
        if (e.affectsConfiguration('q-client') && !e.affectsConfiguration('q-client.term')) {
            window.showInformationMessage('Reload/Restart vscode to Make the Configuration Take Effect.');
        } else if (e.affectsConfiguration('q-server')) {
            const cfg = workspace.getConfiguration('q-server.sourceFiles');
            client.sendNotification('analyzeSourceCode', { globsPattern: cfg.get('globsPattern'), ignorePattern: cfg.get('ignorePattern') });
        }
    });

    // q language server
    const qls = path.join(context.extensionPath, 'dist', 'server.js');

    // The debug options for the server
    // runs the server in Node's Inspector mode for debugging
    const debugOptions = { execArgv: ['--nolazy', '--inspect=6018'] };

    // If launched in debug mode then the debug server options are used
    // Otherwise the run options are used
    const serverOptions: ServerOptions = {
        run: { module: qls, transport: TransportKind.ipc },
        debug: {
            module: qls,
            transport: TransportKind.ipc,
            options: debugOptions
        }
    };

    // Options to control the language client
    const clientOptions: LanguageClientOptions = {
        // Register the server for plain text documents
        documentSelector: [{ scheme: 'file', language: 'q' }],
        synchronize: {
            // Notify the server about q file changes
            fileEvents: workspace.createFileSystemWatcher('**/*.q')
        }
    };

    // Create the language client and start the client.
    const client = new LanguageClient(
        'qLangServer',
        'q Language Server',
        serverOptions,
        clientOptions
    );

    context.subscriptions.push(
        commands.registerCommand('q-client.sendServerCache', code => {
            client.sendNotification('analyzeServerCache', code);
        })
    );

    context.subscriptions.push(
        commands.registerCommand('q-client.sendOnHover', hoverItems => {
            client.sendNotification('prepareOnHover', hoverItems);
        })
    );

    client.start().then(() => {
        const cfg = workspace.getConfiguration('q-server.sourceFiles');
        client.sendNotification('analyzeSourceCode', { globsPattern: cfg.get('globsPattern'), ignorePattern: cfg.get('ignorePattern') });
    });
}
Example #14
Source File: basic.test.ts    From coffeesense with MIT License 4 votes vote down vote up
describe('Should autocomplete', () => {

	it('completes basic properties after dot, partially typed (= no fake line mechanism)', async () => {
		const basic_uri = getDocUri('completion/basic.coffee')
		await testCompletion({ doc_uri: basic_uri, position: position(2, 23), expected_items: ['bbb'] })
		// Inside implicit function call braces
		await testCompletion({ doc_uri: basic_uri, position: position(4, 35), expected_items: ['bbb'] })
		// Inside implicit function call braces, after ().
		await testCompletion({ doc_uri: basic_uri, position: position(6, 25), expected_items: ['toISOString'], allow_unspecified: true, })
	})

	it('completes correctly in implicit return scenario', async () => {
		const doc_uri = getDocUri('completion/implicit-return.coffee')
		await testCompletion({ doc_uri, position: position(6, 41), expected_items: ['ccc1'] })
		await testCompletion({ doc_uri, position: position(6, 40), expected_items: ['bbb1'] })
		await testCompletion({ doc_uri, position: position(6, 37), expected_items: ['bbb1'] })
		await testCompletion({ doc_uri, position: position(6, 36), expected_items: ['bbb1', 'bbb2'] })
	})

	it('completes in optional chaining', async () => {
		const doc_uri = getDocUri('completion/optional-chaining.coffee')
		await testCompletion({ doc_uri, position: position(1, 21), expected_items: ['aaa'] })
	})

	const import_uri = getDocUri('completion/import.coffee')
	it('completes import modules', async () => {
		await testCompletion({ doc_uri: import_uri, position: position(0, 8), expected_items: ['lodash'], allow_unspecified: true, })
		await testCompletion({ doc_uri: import_uri, position: position(1, 10), expected_items: ['lodash'], allow_unspecified: true, })
	})
	for(const p of [[2,11], [3,9], [5,8], [5,22], [5,23], [5,24], [6,52]]) {
		it('completes import module variable names at '+p, async () => {
			await testCompletion({ doc_uri: import_uri, position: position(p[0], p[1]), expected_items: ['findLastIndex'], allow_unspecified: true, })
		})
	}

	it('completes strings', async () => {
		const string_uri = getDocUri('completion/string.coffee')
		await testCompletion({ doc_uri: string_uri, position: position(2, 27), expected_items: ['constant'] })
		await testCompletion({ doc_uri: string_uri, position: position(5, 35), expected_items: ['constant'] })
	})
	it('completes unclosed strings', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/open-string.coffee'), position: position(2, 19), expected_items: ['abc', 'def'] })
		await testCompletion({ doc_uri: getDocUri('completion/open-string-2.coffee'), position: position(2, 21), expected_items: ['abc', 'def'] })
	})

	it('completes open string as inline object assignment', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/open-string-as-inline-object.coffee'), position: position(3, 38), expected_items: ['def', 'ghi'] })
	})

	it('completes open string as inline object param', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/open-string-as-inline-object-param.coffee'), position: position(0, 28), expected_items: ['smooth'] })
	})
	it('completes empty open string as inline object param', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/open-empty-string-as-inline-object-param.coffee'), position: position(0, 27), expected_items: ['smooth', 'auto'] })
	})

	it('completes open string as function param, indented', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/open-string-as-function-param-indented.coffee'), position: position(5, 85), expected_items: ['abc']})
	})

	it('completes open string as function param after opening brace, indented', async () => {
		// TODO should probably specify target range (?) which is 1 char less, so 5,66 and 5,70 - because the `a` is already there
		await testCompletion({ doc_uri: getDocUri('completion/open-string-as-function-param-brace-indented.coffee'), position: position(5, 67), expected_items: ['abc']})
		await testCompletion({ doc_uri: getDocUri('completion/open-string-as-function-param-brace-indented-2.coffee'), position: position(5, 71), expected_items: ['abc']})
	})
	it('completes empty open string as function param after opening brace, indented', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/open-empty-string-as-function-param-brace-indented.coffee'), position: position(5, 72), expected_items: ['abc', 'def'] })
	})

	it('completes for lodash methods', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/external.coffee'), position: position(2, 25), expected_items: ['curry', 'fill'], allow_unspecified: true, })
	})

	it('completes even when the line is invalid', async () => {
		// ...by substituting its contents using fake line logic
		// There is some "definitely_coffee_syntax" in there to avoid this test
		// succeeding merely due to the plain javascript parsing fallback.
		await testCompletion({ doc_uri: getDocUri('completion/fake-line.coffee'), position: position(1, 44), expected_items: ['apply'], allow_unspecified: true, })
	})

	it('completes coffee-only syntax (implicit array) property after dot in fake line when dot is NOT the last char in line but followed by some more content', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/fake-line-array-before-nonsense.coffee'), position: position(0, 48), expected_items: ['flatMap'], allow_unspecified: true, })
	})

	it('completes a param on inline callback with implicit function braces and fake line mechanism', async () => {
		// callback parens insertion in transpileService
		await testCompletion({ doc_uri: getDocUri('completion/inline-callback.coffee'), position: position(0, 65), expected_items: ['toFixed'], allow_unspecified: true, })
	})
	it('completes a param on inline callback with implicit function braces and fake line mechanism and special coffee words like "unless"', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/inline-callback-special-words.coffee'), position: position(0, 128), expected_items: ['toFixed'], allow_unspecified: true, })
	})
	it('completes a param on inline callback with implicit function braces and fake line mechanism and colon in line outside of object', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/inline-callback-colon.coffee'), position: position(0, 75), expected_items: ['abc'] })
	})
	it('completes a param on inline callback with implicit function braces and fake line mechanism and colon in line outside of object with syntax non-understandable to tsserver', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/inline-callback-colon-2.coffee'), position: position(0, 77), expected_items: ['abc'] })
	})
	it('completes a param on inline callback with implicit function braces and fake line mechanism and the line starting with a closing bracket', async () => {
		// This (and also previous test) tests fake line logic by not replacing the entire line with garbage
		// but only removing the trailing dot
		await testCompletion({ doc_uri: getDocUri('completion/inline-callback-bracket.coffee'), position: position(2, 72), expected_items: ['inline_callback_bracket_var_1'] })
	})

	it('completes in assignment', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/assignment.coffee'), position: position(1, 56), expected_items: ['bbb'] })
	})

	it('completes for this.', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/this.coffee'), position: position(3, 13), expected_items: ['bbb', 'ccc'] })
	})

	// same as ^this., but with tabs
	it('completes with tab indentation', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/tab.coffee'), position: position(3, 7), expected_items: ['bbb', 'ccc'] })
	})

	it('completes in fake line after dot even when this line is being combined with the next line by the CS compiler', async () => {
		// This is a more complicated setup most commonly achieved in FP
		await testCompletion({ doc_uri: getDocUri('completion/fake-combined-line.coffee'), position: position(1, 1), expected_items: ['flatMap'], allow_unspecified: true, })
	})

	// bfa0645
	it('completes at @| as this.|', async () => {
		const doc_uri = getDocUri('completion/@.coffee')
		await testCompletion({ doc_uri, position: position(3, 9), expected_items: ['bbb', 'ccc'] })
		await testCompletion({ doc_uri, position: position(4, 15), expected_items: ['bbb', 'ccc'] })
		await testCompletion({ doc_uri, position: position(6, 29), expected_items: ['bbb', 'ccc'] })
		await testCompletion({ doc_uri, position: position(7, 10), expected_items: ['bbb'] })
	})

	// issue #13, #2
	it('parses @ without anything after it as this', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/@.coffee'), position: position(5, 19), expected_items: ['toFixed']})
	})

	it('completes at end of fake line if it contains a @ somewhere earlier', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/@-before-text.coffee'), position: position(4, 32), expected_items: ['abc'] })
	})

	it('completes at end of fake line if it contains a @ somewhere earlier and dot is NOT the last char in line but followed by some more content', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/@-before-text-before-nonsense.coffee'), position: position(4, 48), expected_items: ['abc'] })
	})

	it('completes at the very end of file', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/last-line.coffee'), position: position(1, 14), expected_items: ['toFixed'], allow_unspecified: true, })
	})

	// 4f6a2ed
	it('completes before a comment', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/dot-before-comment.coffee'), position: position(1, 23), expected_items: ['toFixed'], allow_unspecified: true, })
	})

	// bfe46e9
	it('completes after dot in otherwise empty line', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/only-dot.coffee'), position: position(3, 1), expected_items: ['only_dot_obj_prop'] })
	})

	it('completes using javascript parsing fallback when cs compilation failed', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/compilation-fail.coffee'), position: position(3, 21), expected_items: ['toFixed'], allow_unspecified: true, })
	})

	it('completes object properties', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/object.coffee'), position: position(11, 4), expected_items: ['obj_completion_prop_1', 'obj_completion_prop_2'] })
	})

	it('completes object properties before a comment', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/object-before-comment.coffee'), position: position(11, 4), expected_items: ['obj_before_comment_completion_prop_1', 'obj_before_comment_completion_prop_2'] })
	})

	it('completes half defined object properties', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/object-half-defined.coffee'), position: position(12, 4), expected_items: ['obj_halfdefined_completion_prop_2'] })
	})

	it('completes half defined object properties above', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/object-half-defined-above.coffee'), position: position(11, 4), expected_items: ['obj_halfdefined_above_completion_prop_1'] })
	})

	// f5fa3af
	it('completes object properties while current line is invalid', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/object-invalid-line.coffee'), position: position(11, 33), expected_items: ['obj_invalid_line_completion_prop_1', 'obj_invalid_line_completion_prop_2'] })
	})

	it('completes inline object (implicit) property keys as function params even without a colon, while also suggesting local vars', async () => {
		const inline_object_param_key_uri = getDocUri('completion/inline-object-param-key.coffee')
		await testCompletion({
			doc_uri: inline_object_param_key_uri,
			position: position(13, 28),
			expected_items: [
				'obj_inline_param_key_prop_1', // TODO expect pos 1? as there are globals. perhaps always as part of testcompletion
				{ label: 'obj_inline_param_key_unrelated_var', kind: CompletionItemKind.Variable }
			],
			allow_globals: true,
			unexpected_items: [ 'obj_inline_param_key_prop_2' ]
		})
		await testCompletion({
			doc_uri: inline_object_param_key_uri,
			position: position(15, 32),
			expected_items: [
				'obj_inline_param_key_prop_2',
				{ label: 'obj_inline_param_key_unrelated_var', kind: CompletionItemKind.Variable }
			],
			allow_globals: true,
			unexpected_items: [
				'obj_inline_param_key_prop_1',
			]
		})
	})
	it('completes inline object (implicit) property keys in various scenarios', async () => {
		const inline_object_param_key_uri = getDocUri('completion/inline-object-param-key.coffee')
		const checks = [
			[30, 5, 'oi2'], [30, 17, 'oi2'], [30, 18, 'oi2'], [30, 21, ['oi3', 'oi4'], true], [30, 22, ['oi3', 'oi4']],
			[31, 22, ['oi3', 'oi4']], [31, 23, ['oi3', 'oi4']], [31, 24, ['oi3', 'oi4'], true], [31, 25, ['oi3', 'oi4'], true], [31, 26, ['oi3', 'oi4'], true], 
			[32, 22, 'oi4'], [32, 23, ['oi3', 'oi4']], [32, 34, 'oi4'], [32, 35, 'oi4'], 
			[33, 21, ['oi3', 'oi4'], true], 
			[34, 5, 'oi2'], [34, 18, 'oi2'], 
			[35, 20, ['oi3', 'oi4'], true], 
			[36, 16, 'oi2'], 
			[37, 18, ['oi3', 'oi4'], true], 
			[39, 4, ['oi3', 'oi4']], 
			[40, 11, ['oi7', 'oi8'], true], 
			[42, 21, 'oi2'], 
			[43, 8, ['oi1', 'oi2'], true], 
			[46, 7, ['oi11', 'oi12'], true], 
			[49, 19, 'oi15'], 
		] as const
		for(const check of checks) {
			await testCompletion({
				doc_uri: inline_object_param_key_uri,
				position: position(check[0], check[1]),
				//@ts-ignore
				expected_items: Array.isArray(check[2]) ? check[2] : [ check[2] ],
				allow_globals: check[3] || false
			})
		}
	})

	it('completes inline object property keys as function params even without a colon, after opened but not yet closed brace', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/inline-object-open-brace.coffee'), position: position(9, 52), expected_items: ['inline_obj_open_brace_prop_1'] })
	})

	it('does not apply transforms onto jsdoc (exclude comments)', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/jsdoc-spacing.coffee'), position: position(12, 4), expected_items: ['obj_completion_with_jsdoc_spacing_prop_1', 'obj_completion_with_jsdoc_spacing_prop_2', 'obj_completion_with_jsdoc_spacing_prop_3' ] })
	})

	// Known shortcoming - not supported
	it('fails: completes partial object key with no sibling keys', async () => {
		// This does not work because there is no way to know if the partial string is an object key
		// or a full value by itself, so if this is even an object at all.
		await assert.rejects(
			testCompletion({ doc_uri: getDocUri('completion/object-half-line.coffee'), position: position(11, 33), expected_items: ['obj_halfdefined_completion_prop_1', 'obj_halfdefined_completion_prop_2'] }))
	})

	it('completes partial object key with no sibling keys before colon', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/object-half-line-colon.coffee'), position: position(11, 39), expected_items: ['obj_half_line_colon_completion_prop_1', 'obj_half_line_colon_completion_prop_2'] })
	})

	it('completes partial object key with no sibling with braces', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/object-half-line-with-braces.coffee'), position: position(11, 45), expected_items: ['obj_half_line_with_braces_completion_prop_1', 'obj_half_line_with_braces_completion_prop_2'] })
	})

	it('completes partial object key when object closing brace is missing', async () => {
		// This works because it's replaced with `if ... { ...` and then again with the coffee line,
		// and the } from the if-statement closes the coffee line again
		await testCompletion({ doc_uri: getDocUri('completion/object-half-line-with-open-brace.coffee'), position: position(11, 49), expected_items: ['obj_half_line_with_open_brace_completion_prop_1', 'obj_half_line_with_open_brace_completion_prop_2'] })
	})

	it('completes half defined object property when already partially typed', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/object-half-line-half-defined.coffee'), position: position(12, 47), expected_items: ['obj_half_line_half_defined_completion_prop_2'] })
	})

	it('completes half defined object property when already partially typed below', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/object-half-line-half-defined-above.coffee'), position: position(11, 53), expected_items: ['obj_half_line_half_defined_above_completion_prop_1'] })
	})

	it('completes object properties before a statement', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/object-before-statement.coffee'), position: position(11, 4), expected_items: ['obj_completion_before_statement_prop_1', 'obj_completion_before_statement_prop_2'] })
	})

	// 5fede02
	it('maintains proper compilation when an empty line in object is followed by another line with even more indent follows', async () => {
		// this is not really a completion test, more of a syntax check:
		// (a check that '?:?' isn't inserted due to indent follow)
		await testCompletion({ doc_uri: getDocUri('completion/object-before-more-indent.coffee'), position: position(6, 75), expected_items: ['sub_prop'] })
	})

	// ae7693d. I forgot what exactly was going on here, but the test setup is fixed
	it('completes in special case ae7693d', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/ae7693d.coffee'), position: position(2, 13), expected_items: ['ae7693d_obj_sub_prop'] })
	})

	// 4b8257f
	// Also tests fake-line inside if statement
	it('completes in if-statement if next line is indented', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/if.coffee'), position: position(1, 10), expected_items: ['if_obj_prop'] })
	})

	it('completes a variable that has been assigned to inline a if-statement', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/if-assignment.coffee'), position: position(2, 24), expected_items: ['abc'] })
	})

	it('properly inserts a ? before the . when autocompleting with no-unchecked-index-access:true', async () => {
		// the mentioned setting is active for all tests, in jsconfig.json
		await testCompletion({ doc_uri: getDocUri('completion/array-access-with-no-unchecked-index-access.coffee'), position: position(1, 51), expected_items: [{
			label: 'abc',
			insertTextValue: '?.abc',
			textEdit: TextEdit.replace(new Range(new Position(1, 50), new Position(1, 51)), '?.abc')
		}] })
	})

	it('properly replaces the . with [] when autocompleting a complex property that e.g. contains spaces', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/quoted-object-key.coffee'), position: position(2, 22), expected_items: [{
			label: 'a b',
			insertTextValue: '["a b"]',
			textEdit: TextEdit.replace(new Range(new Position(2, 21), new Position(2, 22)), '["a b"]')
		}] })
	})

	// ref issue #1
	it('completes a destructured variable that had a comment block attached, inside a wrapper', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/destructuring-with-comment-block.coffee'), position: position(7, 8), expected_items: ['destructuring_with_comment_block_var_1'] })
	})

	it('completes when there are multiple unclosed curly braces', async () => {
		// this is a pretty random complicated scenario that works by removing all {} and consequently also all ...spread operators
		// (aggressive preprocess)
		await testCompletion({ doc_uri: getDocUri('completion/multiple-open-braces.coffee'), position: position(22, 17), expected_items: ['multiple_open_braces_prop_1', 'multiple_open_braces_prop_1'] })
	})

	it('does not mess up indent in modified-js', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/modified-js-indent.coffee'), position: position(2, 35), expected_items: ['modified_js_indent_prop'] })
	})

	it('completes after dot after closing brace', async () => {
		await testCompletion({ doc_uri: getDocUri('completion/dot-after-closing-brace.coffee'), position: position(1, 14), expected_items: ['splice'], allow_unspecified: true })
	})
})
Example #15
Source File: formattingProvider.ts    From vscode-sqlfluff with MIT License 4 votes vote down vote up
provideDocumentFormattingEdits(
    document: vscode.TextDocument
  ): vscode.TextEdit[] {
    const textEdits: TextEdit[] = [];
    const linterConfiguration = this.linterConfigurationFunc();

    if (linterConfiguration.formatterEnabled) {
      let executable = linterConfiguration.executable;
      let tmp = linterConfiguration.bufferArgs;

      // let args: string[] = ["fix", "--force", document.fileName];
      let args: string[] = ["fix", "--force", "-"];
      let options = vscode.workspace.rootPath
        ? { cwd: vscode.workspace.rootPath }
        : undefined;

      vscode.window.withProgress(
        {
          location: vscode.ProgressLocation.Notification,
          title: "SQLFluff is formatting the file.",
          cancellable: false,
        },
        async (progress, token) => {
          let childProcess = cp.spawn(executable, args, options);
          childProcess.on("error", (error: Error) => {
            let message: string = "";
            if ((<any>error).code === "ENOENT") {
              message = `Cannot lint ${document.fileName}. The executable was not found. Use the 'Executable Path' setting to configure the location of the executable`;
            } else {
              message = error.message
                ? error.message
                : `Failed to run executable using path: ${executable}. Reason is unknown.`;
            }
            vscode.window.showInformationMessage(message);
          });
          let decoder = new LineDecoder();
          let onDataEvent = (data: Buffer) => {
            decoder.formatResultWriter(data);
          };

          let onEndEvent = () => {
            decoder.end();
            let lines = decoder.getLines();
            if (lines && lines.length > 0) {
              const editor = vscode.window.activeTextEditor;
              if (editor) {
                const document = editor.document;
                const selection = editor.selection;

                editor.edit((editBuilder) => {
                  const lineCount = document.lineCount;
                  let lastLineRange = document.lineAt(lineCount - 1).range;
                  const endChar = lastLineRange.end.character;
                  if (lines[0].startsWith("NO SAFETY:")) {
                    lines.shift();
                    lines.shift();
                  }
                  editBuilder.replace(
                    new Range(0, 0, document.lineCount, endChar),
                    lines.join("\n")
                  );
                });
              }
            }
            resolve();
          };

          if (childProcess.pid) {
            childProcess.stdin.write(document.getText());
            childProcess.stdin.end();
            childProcess.stdout.on("data", onDataEvent);
            childProcess.stdout.on("end", onEndEvent);
            resolve();
          } else {
            resolve();
          }
        }
      );
    }
    return textEdits;
  }