vscode#CompletionItem TypeScript Examples

The following examples show how to use vscode#CompletionItem. 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: spDocCompletions.ts    From sourcepawn-vscode with MIT License 6 votes vote down vote up
class SpDocCompletionItem extends CompletionItem {
  constructor(position: Position, FunctionDesc: string[], indent: string) {
    super("/** */", CompletionItemKind.Text);
    let snippet = new SnippetString();
    let max = getMaxLength(FunctionDesc);
    snippet.appendText(`${indent}/**\n ${indent}* `);
    snippet.appendPlaceholder("Description");
    this.appendTextSnippet(snippet, "", indent);
    for (let arg of FunctionDesc) {
      this.appendTextSnippet(
        snippet,
        "@param " + arg + " ".repeat(getSpaceLength(arg, max)),
        indent
      );
      snippet.appendPlaceholder("Param description");
    }
    this.appendTextSnippet(
      snippet,
      "@return " + " ".repeat(getSpaceLengthReturn(max)),
      indent
    );
    snippet.appendPlaceholder("Return description");
    snippet.appendText(`\n${indent}`);
    this.insertText = snippet;
    let start: Position = new Position(position.line, 0);
    let end: Position = new Position(position.line, 0);
    this.range = new Range(start, end);
  }

  private appendTextSnippet(
    snippet: SnippetString,
    text: string,
    indent: string
  ): void {
    snippet.appendText(`\n${indent} * ${text}`);
  }
}
Example #2
Source File: completionProvider.spec.ts    From memo with MIT License 6 votes vote down vote up
describe('resolveCompletionItem()', () => {
  beforeEach(closeEditorsAndCleanWorkspace);

  afterEach(closeEditorsAndCleanWorkspace);

  it('should add documentation for a markdown completion item', async () => {
    const noteName = `note-${rndName()}`;

    const noteUri = await createFile(`${noteName}.md`, 'Test documentation');

    const completionItem: MemoCompletionItem = new CompletionItem(
      noteName,
      CompletionItemKind.File,
    );

    completionItem.fsPath = noteUri!.fsPath;

    expect(
      ((await resolveCompletionItem(completionItem)).documentation as MarkdownString).value,
    ).toBe('Test documentation');
  });

  it('should add documentation for an image completion item', async () => {
    const imageName = `image-${rndName()}`;

    const imageUri = await createFile(`${imageName}.png`);

    const completionItem: MemoCompletionItem = new CompletionItem(
      imageName,
      CompletionItemKind.File,
    );

    completionItem.fsPath = imageUri!.fsPath;

    expect(
      ((await resolveCompletionItem(completionItem)).documentation as MarkdownString).value,
    ).toBe(`![](${Uri.file(completionItem.fsPath).toString()})`);
  });
});
Example #3
Source File: microProfileCompletionItemProvider.ts    From vscode-microprofile with Apache License 2.0 6 votes vote down vote up
/**
 * Provides context-aware completion suggestions for Java files in MicroProfile projects
 */
export class MicroProfileCompletionItemProvider implements CompletionItemProvider<CompletionItem> {

  provideCompletionItems(
      document: TextDocument,
      position: Position,
      _token: CancellationToken,
      _context: CompletionContext): ProviderResult<CompletionItem[] | CompletionList<CompletionItem>> {
    return commands.executeCommand("java.execute.workspaceCommand", JAVA_COMPLETION_REQUEST, adaptToMicroProfileCompletionParams(document, position));
  }

}
Example #4
Source File: neotokyoEvents.ts    From sourcepawn-vscode with MIT License 6 votes vote down vote up
neotokyoEvents: CompletionItem[] = [
	{
		label: "player_death",
		kind: kind,
		detail: detail,
	},
	{
		label: "game_round_end",
		kind: kind,
		detail: detail,
	},
	{
		label: "game_round_start",
		kind: kind,
		detail: detail,
	}
]
Example #5
Source File: lexMode.ts    From yash with MIT License 6 votes vote down vote up
export function getLEXMode(lexLanguageService: LEXLanguageService): LanguageMode {
    const cache = CreateDocumentCache<LexDocument>(10, 60, document => lexLanguageService.parseLexDocument(document));
    return {
        getId() {
            return 'lex';
        },
        doValidation(document: TextDocument): Diagnostic[] {
            const lex = cache.get(document);
            return lexLanguageService.doValidation(document, lex);
        },
        doComplete(document: TextDocument, position: Position): CompletionList | CompletionItem[] {
            const lex = cache.get(document);
            return lexLanguageService.doComplete(document, position, lex);
        },
        doHover(document: TextDocument, position: Position): Hover | null {
            const lex = cache.get(document);
            return lexLanguageService.doHover(document, position, lex);
        },
        findDefinition(document: TextDocument, position: Position): Definition | null {
            const lex = cache.get(document);
            return lexLanguageService.findDefinition(document, position, lex);
        },
        findReferences(document: TextDocument, position: Position): Location[] {
            const lex = cache.get(document);
            return lexLanguageService.findReferences(document, position, lex);
        },
        doRename(document: TextDocument, position: Position, newName: string): WorkspaceEdit | null {
            const lex = cache.get(document);
            return lexLanguageService.doRename(document, position, newName, lex);
        },
        onDocumentRemoved(document: TextDocument) {
            cache.onDocumentRemoved(document);
        },
        dispose() {
            cache.dispose();
        }
    };
}
Example #6
Source File: macroAutocompletionProvider.ts    From vscode-dbt-power-user with MIT License 6 votes vote down vote up
provideCompletionItems(
    document: TextDocument,
    position: Position,
    token: CancellationToken,
    context: CompletionContext
  ): ProviderResult<CompletionItem[] | CompletionList<CompletionItem>> {
    const range = document.getWordRangeAtPosition(position);
    if (range && isEnclosedWithinCodeBlock(document, range)) {
      return this.getAutoCompleteItems(document.uri);
    }
    return undefined;
  }
Example #7
Source File: ALDocCommentProvider.ts    From vscode-alxmldocumentation with MIT License 6 votes vote down vote up
/**
     * Add Completion Item for class XML Documentation snippet.
     * @param alProcedure ALProcedure to document.
     */
    private AddXmlDocCompletionItem(alProcedure: ALProcedure) {
        const completionItem: CompletionItem = new CompletionItem(
            'AL XML Documentation Comment',
            CompletionItemKind.Text
        );

        let snippetText: string = ALDocCommentUtil.GetProcedureDocumentation(alProcedure);

        const snippet: SnippetString = new SnippetString(snippetText.replace('///', '')); // delete leading '///'. The trigger character is already in the document when the completion provider is triggered.
        completionItem.insertText = snippet;
        completionItem.documentation = snippetText;
        completionItem.detail = 'XML documentation comment to document AL procedures.';
        completionItem.sortText = '1';

        this.alXmlDocCompletionItems.push(completionItem);
    }
Example #8
Source File: element-completion-item-povider.ts    From element-ui-helper with MIT License 6 votes vote down vote up
/**
   * 获取属性值的提示信息
   *
   * @param tag 标签
   * @param attr 属性
   */
  getAttrValueCompletionItems(tag: string, attr: string): CompletionItem[] {
    let completionItems: CompletionItem[] = []
    const values = this.getAttrValues(tag, attr)
    values.forEach((value) => {
      if (/\w+/.test(value)) {
        completionItems.push({
          label: `${value}`,
          sortText: `0${value}`,
          detail: `${tag}-${attr}`,
          kind: CompletionItemKind.Value,
          insertText: value
        })
      }
    })
    return completionItems
  }
Example #9
Source File: sfEvents.ts    From sourcepawn-vscode with MIT License 5 votes vote down vote up
sfEvents: CompletionItem[] = [
	{
		label: "player_death",
		kind: kind,
		detail: detail,
	},
	{
		label: "block_frozen",
		kind: kind,
		detail: detail,
	},
	{
		label: "block_unfrozen",
		kind: kind,
		detail: detail,
	},
	{
		label: "phase_switch",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_grab",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_drop",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_score",
		kind: kind,
		detail: detail,
	},
	{
		label: "flag_return",
		kind: kind,
		detail: detail,
	}
]
Example #10
Source File: completionHelper.ts    From coffeesense with MIT License 5 votes vote down vote up
export async function testCompletionResolve(
  docUri: vscode.Uri,
  position: vscode.Position,
  expectedItems: CompletionItem[],
  itemResolveCount = 1,
  matchFn?: (ei: CompletionItem) => (result: CompletionItem) => boolean
) {
  await showFile(docUri);

  const result = (await vscode.commands.executeCommand(
    'vscode.executeCompletionItemProvider',
    docUri,
    position,
    undefined,
    itemResolveCount
  )) as vscode.CompletionList;

  expectedItems.forEach(ei => {
    if (typeof ei === 'string') {
      assert.ok(
        result.items.some(i => i.label === ei),
        `Can't find matching item for\n${JSON.stringify(ei, null, 2)}\nSeen items:\n${JSON.stringify(
          result.items,
          null,
          2
        )}`
      );
    } else {
      const match = matchFn ? result.items.find(matchFn(ei)) : result.items.find(i => i.label === ei.label);
      if (!match) {
        assert.fail(
          `Can't find matching item for\n${JSON.stringify(ei, null, 2)}\nSeen items:\n${JSON.stringify(
            result.items,
            null,
            2
          )}`
        );
      }

      assert.equal(match.label, ei.label);
      if (ei.kind) {
        assert.equal(match.kind, ei.kind);
      }
      if (ei.detail) {
        assert.equal(match.detail, ei.detail);
      }

      if (ei.documentation) {
        if (typeof match.documentation === 'string') {
          assert.equal(normalizeNewline(match.documentation), normalizeNewline(ei.documentation as string));
        } else {
          if (ei.documentation && (ei.documentation as MarkdownString).value && match.documentation) {
            assert.equal(
              normalizeNewline((match.documentation as vscode.MarkdownString).value),
              normalizeNewline((ei.documentation as MarkdownString).value)
            );
          }
        }
      }

      if (ei.additionalTextEdits) {
        assert.strictEqual(match.additionalTextEdits?.length, ei.additionalTextEdits.length);

        ei.additionalTextEdits.forEach((textEdit, i) => {
          assert.strictEqual(match.additionalTextEdits?.[i].newText, textEdit.newText);
          assert.strictEqual(match.additionalTextEdits?.[i].range.start.line, textEdit.range.start.line);
          assert.strictEqual(match.additionalTextEdits?.[i].range.start.character, textEdit.range.start.character);
          assert.strictEqual(match.additionalTextEdits?.[i].range.end.line, textEdit.range.end.line);
          assert.strictEqual(match.additionalTextEdits?.[i].range.end.character, textEdit.range.end.character);
        });
      }
    }
  });
}
Example #11
Source File: spConstantItem.ts    From sourcepawn-vscode with MIT License 5 votes vote down vote up
toCompletionItem(): CompletionItem {
    return {
      label: this.name,
      kind: this.kind,
      detail: "",
    };
  }
Example #12
Source File: ALDocCommentProvider.ts    From vscode-alxmldocumentation with MIT License 5 votes vote down vote up
/**
     * Provide XML Documentation Completion Items (IntelliSense) based on triggering position.
     * @param document Actual Document.
     * @param position Trigger Position.
     * @param token CancellationToken.
     * @param context CompletionContext.
     */
    async provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): Promise<CompletionItem[] | CompletionList<CompletionItem> | null | undefined> {        
        this.alXmlDocCompletionItems = [];

        if (!Configuration.IntelliSenseDocumentationIsEnabled(document.uri)) {
            return;
        }
        
        if (context.triggerCharacter === undefined) {
            return;
        }
        
        let activeLine = ALSyntaxUtil.SplitALCodeToLines(document.getText())[position.line];
        if (activeLine.match(/^[ \t]*\/{3}[ \t]*$/) === null) {
            return;
        }        

        let alObject: ALObject | null = await ALSyntaxUtil.GetALObject(document);
        if (alObject === null) {
            return;
        }

        if (alObject.LineNo > position.line) {     
            this.ProvideALObjectCompletionItems(alObject);
        } else {
            // find procedure
            let alProcedure: ALProcedure | undefined = alObject.Procedures?.find(alProcedure => (alProcedure.LineNo > position.line));
            if (!alProcedure) {
                return;
            }
            
            await this.ProvideALProcedureCompletionItems(alObject, alProcedure);
        }

        return this.alXmlDocCompletionItems;
    }
Example #13
Source File: lexCompletions.ts    From yash with MIT License 5 votes vote down vote up
export function doLEXCompletion(document: TextDocument, position: Position, lexDocument: LexDocument): CompletionItem[] | CompletionList {
    const offset = document.offsetAt(position);
    const text = document.getText();
    const embedded = lexDocument.getEmbeddedCode(offset);
    if (embedded !== undefined) {
        return [];
    }

    const scanner = createScanner(text, offset - 1);
    if (scanner.scan() === TokenType.Percent) {
        if (position.character === 1 && offset < lexDocument.rulesRange[0])
            return keywords.map((keyword) => {
                const completion = new CompletionItem(keyword);
                completion.detail = "keyword";
                completion.kind = CompletionItemKind.Constructor;
                return completion;
            });
        return [];
    }

    const word = document.getText(document.getWordRangeAtPosition(position)).toUpperCase();

    const line = document.lineAt(position.line).text.substring(0, position.character);
    const result: CompletionItem[] = [];
    if (offset < lexDocument.rulesRange[0]) {
        // if before rules zone, definition need to be on the right
        const ok = line.match(/^\w+.*({\w*}?)+/);
        if (ok) {
            Object.keys(lexDocument.defines).filter(t=>t.toUpperCase().startsWith(word)).forEach((key) => {
                const completion = new CompletionItem(key);
                completion.detail = "definition";
                completion.kind = CompletionItemKind.Class;
                result.push(completion);
            })
        }
    } else if (offset < lexDocument.rulesRange[1]) {
        const res = line.match(/^[^\s]*(?:{\w*}?)+$/);
        if (res) {
            if (res[0].length >= position.character) {
                Object.keys(lexDocument.defines).filter(t=>t.toUpperCase().startsWith(word)).forEach((key) => {
                    const completion = new CompletionItem(key);
                    completion.detail = "definition";
                    completion.kind = CompletionItemKind.Class;
                    result.push(completion);
                })
            }
        } else {
            if (line.match(/^<[\w,]*>[^\s]*(?:{\w*}?)+$/)) {
                Object.keys(lexDocument.defines).filter(t=>t.toUpperCase().startsWith(word)).forEach((key) => {
                    const completion = new CompletionItem(key);
                    completion.detail = "definition";
                    completion.kind = CompletionItemKind.Class;
                    result.push(completion);
                })
            } else if (line.match(/^<[\w,]*$/)) { // TODO: fix completion for {} after <>

                Object.keys(lexDocument.states).filter(t=>t.toUpperCase().startsWith(word)).forEach((key) => {
                    const completion = new CompletionItem(key);
                    completion.detail = "initial state";
                    completion.kind = CompletionItemKind.Class;
                    result.push(completion);
                })
            }
        }
    }
    return result;
}
Example #14
Source File: sourceAutocompletionProvider.ts    From vscode-dbt-power-user with MIT License 5 votes vote down vote up
private sourceAutocompleteTableMap: Map<
    string,
    Map<string, CompletionItem[]>
  > = new Map();
Example #15
Source File: completionProvider.ts    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
resolveCompletionItem = sentryReportingCallback(
  async (
    item: CompletionItem,
    token: CancellationToken
  ): Promise<CompletionItem | undefined> => {
    const ctx = "resolveCompletionItem";
    const { label: fname, detail: vname } = item;
    if (
      !_.isString(fname) ||
      !_.isString(vname) ||
      token.isCancellationRequested
    )
      return;

    const engine = ExtensionProvider.getEngine();
    const { vaults, wsRoot } = engine;
    const vault = VaultUtils.getVaultByName({ vname, vaults });
    if (_.isUndefined(vault)) {
      Logger.info({ ctx, msg: "vault not found", fname, vault, wsRoot });
      return;
    }

    const note = NoteUtils.getNoteByFnameFromEngine({
      fname,
      vault,
      engine,
    });

    if (_.isUndefined(note)) {
      Logger.info({ ctx, msg: "note not found", fname, vault, wsRoot });
      return;
    }

    try {
      // Render a preview of this note
      const proc = MDUtilsV5.procRemarkFull(
        {
          dest: DendronASTDest.MD_REGULAR,
          engine,
          vault: note.vault,
          fname: note.fname,
        },
        {
          flavor: ProcFlavor.HOVER_PREVIEW,
        }
      );
      const rendered = await proc.process(
        `![[${VaultUtils.toURIPrefix(note.vault)}/${note.fname}]]`
      );
      if (token.isCancellationRequested) return;
      item.documentation = new MarkdownString(rendered.toString());
      Logger.debug({ ctx, msg: "rendered note" });
    } catch (err) {
      // Failed creating preview of the note
      Logger.info({ ctx, err, msg: "failed to render note" });
      return;
    }

    return item;
  }
)
Example #16
Source File: sourceAutocompletionProvider.ts    From vscode-dbt-power-user with MIT License 5 votes vote down vote up
private sourceAutocompleteNameItemsMap: Map<
    string,
    CompletionItem[]
  > = new Map();
Example #17
Source File: ALDocCommentProvider.ts    From vscode-alxmldocumentation with MIT License 5 votes vote down vote up
private alXmlDocCompletionItems: Array<CompletionItem> = [];
Example #18
Source File: macroAutocompletionProvider.ts    From vscode-dbt-power-user with MIT License 5 votes vote down vote up
private macrosAutocompleteMap: Map<string, CompletionItem[]> = new Map();
Example #19
Source File: spDefineItem.ts    From sourcepawn-vscode with MIT License 5 votes vote down vote up
toCompletionItem(): CompletionItem {
    return {
      label: this.name,
      kind: this.kind,
      detail: this.filePath,
    };
  }
Example #20
Source File: microProfileCompletionItemProvider.ts    From vscode-microprofile with Apache License 2.0 5 votes vote down vote up
provideCompletionItems(
      document: TextDocument,
      position: Position,
      _token: CancellationToken,
      _context: CompletionContext): ProviderResult<CompletionItem[] | CompletionList<CompletionItem>> {
    return commands.executeCommand("java.execute.workspaceCommand", JAVA_COMPLETION_REQUEST, adaptToMicroProfileCompletionParams(document, position));
  }
Example #21
Source File: spFunctionItem.ts    From sourcepawn-vscode with MIT License 5 votes vote down vote up
toCompletionItem(): CompletionItem {
    return {
      label: this.name,
      kind: this.kind,
      detail: basename(this.filePath),
      tags: this.deprecated ? [CompletionItemTag.Deprecated] : [],
    };
  }
Example #22
Source File: completionProvider.ts    From memo with MIT License 4 votes vote down vote up
provideCompletionItems = (document: TextDocument, position: Position) => {
  const linePrefix = document.lineAt(position).text.substr(0, position.character);

  const isResourceAutocomplete = linePrefix.match(/\!\[\[\w*$/);
  const isDocsAutocomplete = linePrefix.match(/\[\[\w*$/);

  if (!isDocsAutocomplete && !isResourceAutocomplete) {
    return undefined;
  }

  const completionItems: MemoCompletionItem[] = [];

  const uris: Uri[] = [
    ...(isResourceAutocomplete
      ? [...cache.getWorkspaceCache().imageUris, ...cache.getWorkspaceCache().markdownUris]
      : []),
    ...(!isResourceAutocomplete
      ? [
          ...cache.getWorkspaceCache().markdownUris,
          ...cache.getWorkspaceCache().imageUris,
          ...cache.getWorkspaceCache().otherUris,
        ]
      : []),
  ];

  const urisByPathBasename = groupBy(uris, ({ fsPath }) => path.basename(fsPath).toLowerCase());

  uris.forEach((uri, index) => {
    const workspaceFolder = workspace.getWorkspaceFolder(uri);

    if (!workspaceFolder) {
      return;
    }

    const longRef = fsPathToRef({
      path: uri.fsPath,
      basePath: workspaceFolder.uri.fsPath,
      keepExt: containsImageExt(uri.fsPath) || containsOtherKnownExts(uri.fsPath),
    });

    const shortRef = fsPathToRef({
      path: uri.fsPath,
      keepExt: containsImageExt(uri.fsPath) || containsOtherKnownExts(uri.fsPath),
    });

    const urisGroup = urisByPathBasename[path.basename(uri.fsPath).toLowerCase()] || [];

    const isFirstUriInGroup =
      urisGroup.findIndex((uriParam) => uriParam.fsPath === uri.fsPath) === 0;

    if (!longRef || !shortRef) {
      return;
    }

    const item = new CompletionItem(longRef, CompletionItemKind.File) as MemoCompletionItem;

    const linksFormat = getMemoConfigProperty('links.format', 'short');

    item.insertText = linksFormat === 'long' || !isFirstUriInGroup ? longRef : shortRef;

    // prepend index with 0, so a lexicographic sort doesn't mess things up
    item.sortText = padWithZero(index);

    item.fsPath = uri.fsPath;

    completionItems.push(item);
  });

  const danglingRefs = cache.getWorkspaceCache().danglingRefs;

  const completionItemsLength = completionItems.length;

  danglingRefs.forEach((ref, index) => {
    const item = new CompletionItem(ref, CompletionItemKind.File);

    item.insertText = ref;

    // prepend index with 0, so a lexicographic sort doesn't mess things up
    item.sortText = padWithZero(completionItemsLength + index);

    completionItems.push(item);
  });

  return completionItems;
}
Example #23
Source File: completionHelper.ts    From coffeesense with MIT License 4 votes vote down vote up
export async function testCompletion({ doc_uri, position, expected_items: expectedItems, match_fn: matchFn, allow_globals, unexpected_items, allow_unspecified }: {
  doc_uri: vscode.Uri,
  position: vscode.Position,
  expected_items: (string | ExpectedCompletionItem)[],
  unexpected_items?: string[],
  allow_unspecified?: boolean,
  allow_globals?: boolean,
  match_fn?: (ei: string | ExpectedCompletionItem) => (result: CompletionItem) => boolean,
}) {
  await showFile(doc_uri);

  const result = (await vscode.commands.executeCommand(
    'vscode.executeCompletionItemProvider',
    doc_uri,
    position
  )) as vscode.CompletionList;

  if(!allow_unspecified && !allow_globals)
    //@ts-ignore
    assert.equal(expectedItems.length, result.items.filter(i => i.label.label !== '#region' && i.label.label !== '#endregion' && i.label !== '#region' && i.label !== '#endregion').length)

  if(!allow_globals) {
    // We never want to see global suggestions, like DOM:
    // This is because 1. it can yield false positives from import suggestions
    // for fields that should have been suggested from other sources instead, and
    // 2. it almost always means that some scoping is wrong.
    assert.ok(! result.items.some(i => i.label === 'AbortController' || i.label === 'encodeURIComponent'))
    // With lodash, there can be as many as 396 (2022-03)
    assert.ok(result.items.length < 450)
  }

  if(unexpected_items?.length)
    // @ts-ignore
    assert.ok(! result.items.some(i => unexpected_items.includes(i.label.label? i.label.label : i.label)))

  expectedItems.forEach(ei => {
    let match_index = -1
    if (typeof ei === 'string') {
      match_index = result.items.findIndex(i => {
          return i.label === ei &&
            // Omit standard matches like variable as these primarily yield false positives.
            // If these are really required, they can be passed separately.
            [CompletionItemKind.Function, CompletionItemKind.Property, CompletionItemKind.Field].includes(i.kind || -1)
        })
      assert.ok(match_index > -1,
        `Can't find matching item for\n${JSON.stringify(ei, null, 2)}\nSeen items:\n${JSON.stringify(
          result.items,
          null,
          2
        )}`
      );
    } else {
      const match_index = matchFn ? result.items.findIndex(matchFn(ei)) : result.items.findIndex(i => i.label === ei.label);
      const match = result.items[match_index]
      if (!match) {
        assert.fail(
          `Can't find matching item for\n${JSON.stringify(ei, null, 2)}\nSeen items:\n${JSON.stringify(
            result.items,
            null,
            2
          )}`
        );
      }

      assert.equal(match.label, ei.label);
      if (ei.kind) {
        assert.equal(match.kind, ei.kind);
      }
      if (ei.detail) {
        assert.equal(match.detail, ei.detail);
      }

      if (ei.documentation) {
        if (typeof match.documentation === 'string') {
          assert.equal(normalizeNewline(match.documentation), normalizeNewline(ei.documentation as string));
        } else {
          if (ei.documentation && (ei.documentation as MarkdownString).value && match.documentation) {
            assert.equal(
              normalizeNewline((match.documentation as vscode.MarkdownString).value),
              normalizeNewline((ei.documentation as MarkdownString).value)
            );
          }
        }
      }

      if (ei.documentationStart) {
        if (typeof match.documentation === 'string') {
          assert.ok(
            match.documentation.startsWith(ei.documentationStart),
            `${match.documentation}\ndoes not start with\n${ei.documentationStart}`
          );
        } else {
          assert.ok(
            (match.documentation as vscode.MarkdownString).value.startsWith(ei.documentationStart),
            `${(match.documentation as vscode.MarkdownString).value}\ndoes not start with\n${ei.documentationStart}`
          );
        }
      }

      if (ei.documentationFragment) {
        if (typeof match.documentation === 'string') {
          assert.ok(
            match.documentation.includes(ei.documentationFragment),
            `${match.documentation}\ndoes not include\n${ei.documentationFragment}`
          );
        } else {
          assert.ok(
            (match.documentation as vscode.MarkdownString).value.includes(ei.documentationFragment),
            `${(match.documentation as vscode.MarkdownString).value}\ndoes not include\n${ei.documentationFragment}`
          );
        }
      }

      if (ei.insertTextValue) {
        if (match.insertText instanceof vscode.SnippetString) {
          assert.strictEqual(match.insertText.value, ei.insertTextValue);
        } else {
          assert.strictEqual(match.insertText, ei.insertTextValue);
        }
      }

      if (ei.textEdit) {
        assert.strictEqual(match.textEdit?.newText, ei.textEdit.newText)
        assert.strictEqual(match.textEdit?.range.start.line, ei.textEdit.range.start.line)
        assert.strictEqual(match.textEdit?.range.start.character, ei.textEdit.range.start.character)
        assert.strictEqual(match.textEdit?.range.end.line, ei.textEdit.range.end.line)
        assert.strictEqual(match.textEdit?.range.end.character, ei.textEdit.range.end.character)
      }
    }

    if(!allow_unspecified)
      assert.ok(match_index < expectedItems.length, `Expected item found but after unspecified items! match_index ${match_index} >= expectedItems.length ${expectedItems.length}`)
  });
}
Example #24
Source File: completionProviders.ts    From vscode-todo-md with MIT License 4 votes vote down vote up
/**
 * Update editor autocomplete/suggest
 */
export function updateCompletions() {
	Global.tagAutocompleteDisposable?.dispose();
	Global.projectAutocompleteDisposable?.dispose();
	Global.contextAutocompleteDisposable?.dispose();
	Global.generalAutocompleteDisposable?.dispose();
	Global.specialTagsAutocompleteDisposable?.dispose();

	Global.tagAutocompleteDisposable = languages.registerCompletionItemProvider(
		getTodoMdFileDocumentSelector(),
		{
			provideCompletionItems(document: TextDocument, position: Position) {
				const wordAtCursor = getWordAtPosition(document, position);
				if (!wordAtCursor || !wordAtCursor.startsWith('#')) {
					return undefined;
				}
				const tagCompletions = [];
				const tags = unique($state.tags.concat(Object.keys($state.suggestTags)));
				for (const tag of tags) {
					const tagCompletion = new CompletionItem(tag, CompletionItemKind.Field);
					const documentation = new MarkdownString($state.suggestTags[tag], true);
					documentation.isTrusted = true;
					tagCompletion.documentation = documentation;
					tagCompletion.insertText = `${tag} `;
					tagCompletions.push(tagCompletion);
				}

				return tagCompletions;
			},
		},
		'#',
	);
	Global.projectAutocompleteDisposable = languages.registerCompletionItemProvider(
		getTodoMdFileDocumentSelector(),
		{
			provideCompletionItems(document: TextDocument, position: Position) {
				const wordAtCursor = getWordAtPosition(document, position);
				if (!wordAtCursor || !wordAtCursor.startsWith('+')) {
					return undefined;
				}
				const projectCompletions = [];
				const projects = unique($state.projects.concat(Object.keys($state.suggestProjects)));
				for (const project of projects) {
					const projectCompletion = new CompletionItem(project, CompletionItemKind.Field);
					const documentation = new MarkdownString($state.suggestProjects[project], true);
					documentation.isTrusted = true;
					projectCompletion.documentation = documentation;
					projectCompletion.insertText = `${project} `;
					projectCompletions.push(projectCompletion);
				}

				return projectCompletions;
			},
		},
		'+',
	);
	Global.contextAutocompleteDisposable = languages.registerCompletionItemProvider(
		getTodoMdFileDocumentSelector(),
		{
			provideCompletionItems(document: TextDocument, position: Position) {
				const wordAtCursor = getWordAtPosition(document, position);
				if (!wordAtCursor || !wordAtCursor.startsWith('@')) {
					return undefined;
				}
				const contextCompletions = [];
				const contexts = unique($state.contexts.concat(Object.keys($state.suggestContexts)));
				for (const context of contexts) {
					const contextCompletion = new CompletionItem(context, CompletionItemKind.Field);
					const documentation = new MarkdownString($state.suggestContexts[context], true);
					documentation.isTrusted = true;
					contextCompletion.documentation = documentation;
					contextCompletion.insertText = `${context} `;
					contextCompletions.push(contextCompletion);
				}

				return contextCompletions;
			},
		},
		'@',
	);
	Global.generalAutocompleteDisposable = languages.registerCompletionItemProvider(
		getTodoMdFileDocumentSelector(),
		{
			provideCompletionItems(document: TextDocument, position: Position) {
				const today = new CompletionItem('TODAY', CompletionItemKind.Constant);
				today.insertText = getDateInISOFormat(new Date());

				const setDueDateToday = new CompletionItem('SET_DUE_TODAY', CompletionItemKind.Constant);
				setDueDateToday.insertText = helpCreateSpecialTag(SpecialTagName.Due, getDateInISOFormat(new Date()));

				const setDueDateTomorrow = new CompletionItem('SET_DUE_TOMORROW', CompletionItemKind.Constant);
				setDueDateTomorrow.insertText = helpCreateSpecialTag(SpecialTagName.Due, getDateInISOFormat(dayjs().add(1, 'day')));

				const setDueDateYesterday = new CompletionItem('SET_DUE_YESTERDAY', CompletionItemKind.Constant);
				setDueDateYesterday.insertText = helpCreateSpecialTag(SpecialTagName.Due, getDateInISOFormat(dayjs().subtract(1, 'day')));

				const setDueDateThisWeek = new CompletionItem('SET_DUE_THIS_WEEK', CompletionItemKind.Constant);
				setDueDateThisWeek.insertText = helpCreateSpecialTag(SpecialTagName.Due, helpCreateDueDate('this week'));

				const setDueDateNextWeek = new CompletionItem('SET_DUE_NEXT_WEEK', CompletionItemKind.Constant);
				setDueDateNextWeek.insertText = helpCreateSpecialTag(SpecialTagName.Due, helpCreateDueDate('next week'));

				const weekdayCompletions: CompletionItem[] = weekdayNamesLong.map(weekdayName => {
					const setDueDateWeekday = new CompletionItem(`SET_DUE_${weekdayName.toUpperCase()}`, CompletionItemKind.Constant);
					setDueDateWeekday.insertText = helpCreateSpecialTag(SpecialTagName.Due, helpCreateDueDate(weekdayName));
					return setDueDateWeekday;
				});

				return [
					...weekdayCompletions,
					today,
					setDueDateToday,
					setDueDateTomorrow,
					setDueDateYesterday,
					setDueDateThisWeek,
					setDueDateNextWeek,
				];
			},
		},
		'',
	);
	Global.specialTagsAutocompleteDisposable = languages.registerCompletionItemProvider(
		getTodoMdFileDocumentSelector(),
		{
			provideCompletionItems(document: TextDocument, position: Position) {
				const charBeforeCursor = document.getText(new Range(position.line, position.character === 0 ? 0 : position.character - 1, position.line, position.character));
				if (charBeforeCursor !== '{') {
					return undefined;
				}
				const specialTags = [
					SpecialTagName.Collapsed,
					SpecialTagName.CompletionDate,
					SpecialTagName.Count,
					SpecialTagName.CreationDate,
					SpecialTagName.Due,
					SpecialTagName.Duration,
					SpecialTagName.Hidden,
					SpecialTagName.Overdue,
					SpecialTagName.Started,
				];

				const specialTagCompletionItems = [];

				for (const specialTag of specialTags) {
					const completionItem = new CompletionItem(specialTag, CompletionItemKind.Field);
					completionItem.detail = specialTagDescription[specialTag];
					specialTagCompletionItems.push(completionItem);
				}

				return specialTagCompletionItems;
			},
		},
		'{',
	);
	Global.setDueDateAutocompleteDisposable = languages.registerCompletionItemProvider(
		getTodoMdFileDocumentSelector(),
		{
			provideCompletionItems(document: TextDocument, position: Position) {
				const wordRange = getWordRangeAtPosition(document, position);
				const wordAtCursor = getWordAtPosition(document, position);
				if (!wordAtCursor) {
					return undefined;
				}

				if (wordAtCursor[wordAtCursor.length - 1] === '$') {
					const dueDate = helpCreateDueDate(wordAtCursor.slice(0, -1));
					if (!dueDate) {
						return [];
					}
					const completionItem = new CompletionItem(new DueDate(dueDate).closestDueDateInTheFuture, CompletionItemKind.Constant);
					completionItem.insertText = '';
					completionItem.filterText = wordAtCursor;
					completionItem.command = {
						command: 'todomd.setDueDateWithArgs',
						title: 'Set Due Date with arguments',
						arguments: [
							document,
							wordRange,
							dueDate,
						],
					};
					return [completionItem];
				} else {
					return [];
				}
			},
		},
		'$',
	);
}
Example #25
Source File: NextObjectIdCompletionItem.ts    From al-objid with MIT License 4 votes vote down vote up
export class NextObjectIdCompletionItem extends CompletionItem {
    private _injectSemicolon: boolean = false;
    private _range: NinjaALRange | undefined;

    private isIdEqual(left: number | number[], right: number) {
        let leftAsArray = left as number[];

        switch (true) {
            case typeof left === "number":
                return left === right;

            case Array.isArray(left):
                return leftAsArray.length === 1 && leftAsArray[0] === right;
        }

        return false;
    }

    constructor(
        type: string,
        objectId: NextObjectIdInfo,
        app: ALApp,
        position: Position,
        uri: Uri,
        nextIdContext: NextIdContext,
        range?: NinjaALRange
    ) {
        super(`${objectId.id}${nextIdContext.injectSemicolon ? ";" : ""}`, CompletionItemKind.Constant);

        this._injectSemicolon = nextIdContext.injectSemicolon;
        this._range = range;

        this.sortText = nextIdContext.additional ? `0.${nextIdContext.additional.ordinal / 1000}` : "0";
        this.command = this.getCompletionCommand(position, uri, type, app, objectId);
        this.documentation = this.getCompletionDocumentation(type, objectId);
        this.insertText = `${objectId.id}${this._injectSemicolon ? ";" : ""}`;
        this.detail = "AL Object ID Ninja";
        this.label = range && range.description ? `${objectId.id} (${range.description})` : this.insertText;
        this.kind = CompletionItemKind.Constant;
    }

    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);
                },
            ],
        };
    }

    private nextIdDescription(type: string): string {
        const parts = type.split("_");

        if (parts.length === 1) {
            return type;
        }

        let id = "";
        switch (parts[0]) {
            case "table":
                id = "field ID";
                break;
            case "enum":
                id = "value ID";
                break;
        }

        return `${id} from ${parts[0]} ${parts[1]}`;
    }

    getCompletionDocumentation(type: string, objectId: NextObjectIdInfo): MarkdownString {
        const firstLine = `Assigns the next available ${this.nextIdDescription(type)} from the Azure back end.`;
        let typeDesc = `${type} ${objectId.id}`;
        if (type.startsWith("table_")) {
            typeDesc = `field(${objectId.id}; ...)`;
        } else if (type.startsWith("enum_")) {
            typeDesc = `value(${objectId.id}; ...)`;
        }

        const message = new MarkdownString(firstLine);
        message.appendCodeblock(typeDesc, "al");
        if (!objectId.hasConsumption) {
            message.appendMarkdown(
                "**Important:** The back end has no object ID consumption information. Please, run `Ninja: Synchronize object IDs with the Azure back end` command before accepting this object ID."
            );
            return message;
        }
        message.appendMarkdown(
            "This number is **temporary**. The actual number will be assigned when you select this auto-complete entry."
        );

        return message;
    }
}
Example #26
Source File: CompletionProvider.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("completionProvider", function () {
  describeMultiWS(
    "wikilink",
    {
      preSetupHook: async (opts) => {
        const { wsRoot, vaults } = opts;
        await NoteTestUtilsV4.createNote({
          fname: "test",
          vault: vaults[1],
          wsRoot,
        });
        await ENGINE_HOOKS.setupBasic(opts);
      },
    },
    () => {
      test("THEN provide completions", async () => {
        const { wsRoot, engine, vaults } = ExtensionProvider.getDWorkspace();
        // Open a note, add [[]]
        await WSUtils.openNote(
          NoteUtils.getNoteOrThrow({
            fname: "root",
            vault: vaults[1],
            wsRoot,
            notes: engine.notes,
          })
        );
        const editor = VSCodeUtils.getActiveTextEditorOrThrow();
        await editor.edit((editBuilder) => {
          editBuilder.insert(new Position(8, 0), "[[]]");
        });
        // have the completion provider complete this wikilink
        const items = provideCompletionItems(
          editor.document,
          new Position(8, 2)
        );
        expect(items).toBeTruthy();
        // Suggested all the notes
        expect(items!.length).toEqual(7);
        for (const item of items!) {
          // All suggested items exist
          const found = NoteUtils.getNotesByFnameFromEngine({
            fname: item.label as string,
            engine,
          });
          expect(found.length > 0).toBeTruthy();
        }
        // check that same vault items are sorted before other items
        const sortedItems = _.sortBy(
          items,
          (item) => item.sortText || item.label
        );
        const testIndex = _.findIndex(
          sortedItems,
          (item) => item.label === "test"
        );
        expect(testIndex !== -1 && testIndex < 2).toBeTruthy();
        // Check that xvault links were generated where needed, and only where needed.
        // Using root notes since they are in every vault.
        const rootItems = _.filter(items, (item) => item.label === "root");
        for (const item of rootItems) {
          if (item.detail === VaultUtils.getName(vaults[1])) {
            // don't need an xvault link, should be a regular one
            expect(item.insertText).toEqual(item.label);
            expect(
              (item.insertText as string).startsWith(
                CONSTANTS.DENDRON_DELIMETER
              )
            ).toBeFalsy();
          } else {
            // does need an xvault link
            expect(
              (item.insertText as string).startsWith(
                CONSTANTS.DENDRON_DELIMETER
              )
            ).toBeTruthy();
          }
        }
      });
    }
  );

  describeMultiWS(
    "GIVEN hashtag",
    {
      preSetupHook: async (opts) => {
        const { wsRoot, vaults } = opts;
        await NoteTestUtilsV4.createNote({
          fname: "tags.foo",
          vault: vaults[1],
          wsRoot,
        });
        await NoteTestUtilsV4.createNote({
          fname: "tags.bar",
          vault: vaults[1],
          wsRoot,
        });
      },
    },
    () => {
      test("THEN provide correct completion", async () => {
        const { wsRoot, engine, vaults } = ExtensionProvider.getDWorkspace();
        // Open a note, add [[]]
        await WSUtils.openNote(
          NoteUtils.getNoteOrThrow({
            fname: "root",
            vault: vaults[1],
            wsRoot,
            notes: engine.notes,
          })
        );
        const editor = VSCodeUtils.getActiveTextEditorOrThrow();
        await editor.edit((editBuilder) => {
          editBuilder.insert(new Position(8, 0), "#");
        });
        // have the completion provider complete this wikilink
        const items = provideCompletionItems(
          editor.document,
          new Position(8, 1)
        );
        expect(items).toBeTruthy();
        // Suggested all the notes
        expect(items!.length).toEqual(2);
        for (const item of items!) {
          // All suggested items exist
          const found = NoteUtils.getNotesByFnameFromEngine({
            fname: `${TAGS_HIERARCHY}${item.label}`,
            engine,
          });
          expect(found.length > 0).toBeTruthy();
        }
      });
    }
  );

  describeMultiWS(
    "GIVEN hashtag that's in a sentence",
    {
      preSetupHook: async (opts) => {
        const { wsRoot, vaults } = opts;
        await NoteTestUtilsV4.createNote({
          fname: "tags.foo",
          vault: vaults[1],
          wsRoot,
        });
        await NoteTestUtilsV4.createNote({
          fname: "tags.bar",
          vault: vaults[1],
          wsRoot,
        });
      },
    },
    () => {
      test("THEN provide correct completions", async () => {
        const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
        // Open a note, add [[]]
        await WSUtils.openNote(
          NoteUtils.getNoteOrThrow({
            fname: "root",
            vault: vaults[1],
            wsRoot,
            notes: engine.notes,
          })
        );
        const editor = VSCodeUtils.getActiveTextEditorOrThrow();
        await editor.edit((editBuilder) => {
          editBuilder.insert(new Position(8, 0), "Lorem ipsum #");
        });
        // have the completion provider complete this wikilink
        const items = provideCompletionItems(
          editor.document,
          new Position(8, 13)
        );
        expect(items).toBeTruthy();
        // Suggested all the notes
        expect(items!.length).toEqual(2);
        for (const item of items!) {
          // All suggested items exist
          const found = NoteUtils.getNotesByFnameFromEngine({
            fname: `${TAGS_HIERARCHY}${item.label}`,
            engine,
          });
          expect(found.length > 0).toBeTruthy();
        }
      });
    }
  );

  describeMultiWS(
    "user tag",
    {
      preSetupHook: async (opts) => {
        const { wsRoot, vaults } = opts;
        await NoteTestUtilsV4.createNote({
          fname: "user.foo",
          vault: vaults[1],
          wsRoot,
        });
        await NoteTestUtilsV4.createNote({
          fname: "user.bar",
          vault: vaults[1],
          wsRoot,
        });
      },
    },
    () => {
      test("THEN provide correct completions", async () => {
        const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
        // Open a note, add [[]]
        await WSUtils.openNote(
          NoteUtils.getNoteOrThrow({
            fname: "root",
            vault: vaults[1],
            wsRoot,
            notes: engine.notes,
          })
        );
        const editor = VSCodeUtils.getActiveTextEditorOrThrow();
        await editor.edit((editBuilder) => {
          editBuilder.insert(new Position(8, 0), "@");
        });
        // have the completion provider complete this wikilink
        const items = provideCompletionItems(
          editor.document,
          new Position(8, 1)
        );
        expect(items).toBeTruthy();
        // Suggested all the notes
        expect(items!.length).toEqual(2);
        for (const item of items!) {
          // All suggested items exist
          const found = NoteUtils.getNotesByFnameFromEngine({
            fname: `${USERS_HIERARCHY}${item.label}`,
            engine,
          });
          expect(found.length > 0).toBeTruthy();
        }
      });
    }
  );

  describeMultiWS(
    "WHEN completing a wikilink without closing brackets",
    {},
    () => {
      let items: CompletionItem[] | undefined;
      before(async () => {
        const { vaults, wsRoot, engine } = getDWorkspace();
        await WSUtils.openNote(
          NoteUtils.getNoteOrThrow({
            fname: "root",
            vault: vaults[1],
            wsRoot,
            notes: engine.notes,
          })
        );
        const editor = VSCodeUtils.getActiveTextEditorOrThrow();
        await editor.edit((editBuilder) => {
          editBuilder.insert(new Position(8, 0), "Commodi [[ nam");
        });
        items = provideCompletionItems(editor.document, new Position(8, 10));
      });

      test("THEN it finds completions", () => {
        expect(items?.length).toEqual(3);
      });

      test("THEN it doesn't erase any text following the wikilink", async () => {
        for (const item of items!) {
          const range = item.range! as Range;
          // Since there's no text, start and end of range is at the same place.
          // The end doesn't go over the following text to avoid deleting them, since those are not part of the wikilink.
          expect(range.start.character).toEqual(10);
          expect(range.end.character).toEqual(10);
        }
      });

      test("THEN it adds the closing brackets", async () => {
        for (const item of items!) {
          expect(item.insertText!.toString().endsWith("]]")).toBeTruthy();
        }
      });
    }
  );

  runTestButSkipForWindows()("blocks", () => {
    describeMultiWS(
      "",
      {
        preSetupHook: ENGINE_HOOKS.setupBasic,
      },
      () => {
        test("THEN doesn't provide outside wikilink", async () => {
          const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
          // Open a note, add [[]]
          await WSUtils.openNote(
            NoteUtils.getNoteOrThrow({
              fname: "root",
              vault: vaults[0],
              wsRoot,
              notes: engine.notes,
            })
          );
          const editor = VSCodeUtils.getActiveTextEditorOrThrow();
          await editor.edit((editBuilder) => {
            editBuilder.insert(new Position(8, 0), "^");
          });
          // have the completion provider complete this wikilink
          const items = await provideBlockCompletionItems(
            editor.document,
            new Position(8, 1)
          );
          expect(items).toEqual(undefined);
        });
      }
    );

    describeMultiWS(
      "GIVEN paragraphs",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test",
            body: [
              "Et et quam culpa.",
              "",
              "Cumque molestiae qui deleniti.",
              "Eius odit commodi harum.",
              "",
              "Sequi ut non delectus tempore.",
            ].join("\n"),
          });
        },
      },
      () => {
        test("THEN provide correct completions", async () => {
          const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
          // Open a note, add [[^]]
          await WSUtils.openNote(
            NoteUtils.getNoteOrThrow({
              fname: "test",
              vault: vaults[0],
              wsRoot,
              notes: engine.notes,
            })
          );
          const editor = VSCodeUtils.getActiveTextEditorOrThrow();
          await editor.edit((editBuilder) => {
            editBuilder.insert(new Position(8, 0), "[[^]]");
          });
          // have the completion provider complete this wikilink
          const items = await provideBlockCompletionItems(
            editor.document,
            new Position(8, 3)
          );
          expect(items).toBeTruthy();
          expect(items?.length).toEqual(3);
          // check that the
        });
      }
    );

    describeMultiWS(
      "GIVEN nested list",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test",
            body: [
              "Et et quam culpa.",
              "",
              "* Cumque molestiae qui deleniti.",
              "* Eius odit commodi harum.",
              "  * Sequi ut non delectus tempore.",
              "  * In delectus quam sunt unde.",
              "* Quasi ex debitis aut sed.",
              "",
              "Perferendis officiis ut non.",
            ].join("\n"),
          });
        },
      },
      () => {
        test("THEN provide correct completions", async () => {
          const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
          // Open a note, add [[^]]
          await WSUtils.openNote(
            NoteUtils.getNoteOrThrow({
              fname: "test",
              vault: vaults[0],
              wsRoot,
              notes: engine.notes,
            })
          );
          const editor = VSCodeUtils.getActiveTextEditorOrThrow();
          await editor.edit((editBuilder) => {
            editBuilder.insert(new Position(8, 0), "[[^]]");
          });
          // have the completion provider complete this wikilink
          const items = await provideBlockCompletionItems(
            editor.document,
            new Position(8, 3)
          );
          expect(items).toBeTruthy();
          expect(items?.length).toEqual(8);
        });
      }
    );

    // TODO: flaky
    test.skip("provides headers for other files", (done) => {
      runLegacyMultiWorkspaceTest({
        onInit: async ({ wsRoot, vaults, engine }) => {
          // Open a note, add [[test2#]]
          await WSUtils.openNote(
            NoteUtils.getNoteOrThrow({
              fname: "test",
              vault: vaults[0],
              wsRoot,
              notes: engine.notes,
            })
          );
          const editor = VSCodeUtils.getActiveTextEditorOrThrow();
          await editor.edit((editBuilder) => {
            editBuilder.insert(new Position(7, 0), "[[test2#]]");
          });
          // have the completion provider complete this wikilink
          const items = await provideBlockCompletionItems(
            editor.document,
            new Position(7, 3)
          );
          expect(items).toBeTruthy();
          expect(items?.length).toEqual(2);
          expect(items![0].insertText).toEqual("et-et-quam-culpa");
          expect(items![1].insertText).toEqual("quasi-ex-debitis-aut-sed");
          done();
        },
        preSetupHook: async ({ wsRoot, vaults }) => {
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test2",
            body: [
              "## Et et quam culpa.",
              "",
              "* Cumque molestiae qui deleniti.",
              "* Eius odit commodi harum.",
              "  * Sequi ut non delectus tempore.",
              "  * In delectus quam sunt unde.",
              "",
              "## Quasi ex debitis aut sed.",
              "",
              "Perferendis officiis ut non.",
            ].join("\n"),
          });
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test",
          });
        },
      });
    });

    describeMultiWS(
      "GIVEN other files with block anchors",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test2",
            body: [
              "Et et quam culpa.",
              "",
              "* Cumque molestiae qui deleniti.",
              "* Eius odit commodi harum. ^item-2",
              "  * Sequi ut non delectus tempore.",
              "  * In delectus quam sunt unde. ^item-4",
              "",
              "Quasi ex debitis aut sed.",
              "",
              "Perferendis officiis ut non. ^last-paragraph",
            ].join("\n"),
          });
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test",
          });
        },
      },
      () => {
        test("THEN provide correct completions", async () => {
          const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
          // Open a note, add [[test2#^]]
          await WSUtils.openNote(
            NoteUtils.getNoteOrThrow({
              fname: "test",
              vault: vaults[0],
              wsRoot,
              notes: engine.notes,
            })
          );
          const editor = VSCodeUtils.getActiveTextEditorOrThrow();
          await editor.edit((editBuilder) => {
            editBuilder.insert(new Position(7, 0), "[[test2#^]]");
          });
          // have the completion provider complete this wikilink
          const items = await provideBlockCompletionItems(
            editor.document,
            new Position(7, 3)
          );
          expect(items).toBeTruthy();
          expect(items?.length).toEqual(3);
          expect(items![0].insertText).toEqual("item-2");
          expect(items![1].insertText).toEqual("item-4");
          expect(items![2].insertText).toEqual("last-paragraph");
        });
      }
    );

    function hasNoEditContaining(
      item: CompletionItem,
      newTextSubString: string
    ) {
      expect(
        _.find(
          item.additionalTextEdits,
          (edit) => edit.newText.indexOf(newTextSubString) !== -1
        )
      ).toEqual(undefined);
    }

    describeMultiWS(
      "",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test",
            body: [
              "# Et et quam culpa. ^header",
              "",
              "Ullam vel eius reiciendis. ^paragraph",
              "",
              "* Cumque molestiae qui deleniti. ^item1",
              "* Eius odit commodi harum. ^item2",
              "  * Sequi ut non delectus tempore. ^item3",
              "",
              "^list",
              "",
              "| Sapiente | accusamus |",
              "|----------|-----------|",
              "| Laborum  | libero    |",
              "| Ullam    | optio     | ^table",
            ].join("\n"),
          });
        },
      },
      () => {
        test("THEN provide correct completions", async () => {
          const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
          // Open a note, add [[^]]
          await WSUtils.openNote(
            NoteUtils.getNoteOrThrow({
              fname: "test",
              vault: vaults[0],
              wsRoot,
              notes: engine.notes,
            })
          );
          const editor = VSCodeUtils.getActiveTextEditorOrThrow();
          await editor.edit((editBuilder) => {
            editBuilder.insert(new Position(8, 0), "[[^]]");
          });
          // have the completion provider complete this wikilink
          const items = await provideBlockCompletionItems(
            editor.document,
            new Position(8, 3)
          );
          // Check that the correct anchors were returned
          expect(items).toBeTruthy();
          expect(items!.length).toEqual(7);
          expect(items![0].insertText).toEqual("#et-et-quam-culpa");
          expect(items![1].insertText).toEqual("#^paragraph");
          expect(items![2].insertText).toEqual("#^item1");
          expect(items![3].insertText).toEqual("#^item2");
          expect(items![4].insertText).toEqual("#^item3");
          expect(items![5].insertText).toEqual("#^list");
          expect(items![6].insertText).toEqual("#^table");
          // check that we're not trying to insert unnecessary anchors
          hasNoEditContaining(items![0], "et-et-quam-culpa");
          hasNoEditContaining(items![0], "^");
          hasNoEditContaining(items![1], "^paragraph");
          hasNoEditContaining(items![2], "^item1");
          hasNoEditContaining(items![3], "^item2");
          hasNoEditContaining(items![4], "^item3");
          hasNoEditContaining(items![5], "^list");
          hasNoEditContaining(items![6], "^table");
        });
      }
    );
  });
});
Example #27
Source File: sourceGeneric.ts    From sourcepawn-vscode with MIT License 4 votes vote down vote up
sourceGenericEvents: CompletionItem[] = [
	{
		label: "team_info",
		kind: kind,
		detail: detail,
	},
	{
		label: "team_score",
		kind: kind,
		detail: detail,
	},
	{
		label: "teamplay_broadcast_audio",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_team",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_class",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_death",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_hurt",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_chat",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_score",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_spawn",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_shoot",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_use",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_changename",
		kind: kind,
		detail: detail,
	},
	{
		label: "player_hintmessage",
		kind: kind,
		detail: detail,
	},
	{
		label: "base_player_teleported",
		kind: kind,
		detail: detail,
	},
	{
		label: "game_init",
		kind: kind,
		detail: detail,
	},
	{
		label: "game_newmap",
		kind: kind,
		detail: detail,
	},
	{
		label: "game_start",
		kind: kind,
		detail: detail,
	},
	{
		label: "game_end",
		kind: kind,
		detail: detail,
	},
	{
		label: "round_start",
		kind: kind,
		detail: detail,
	},
	{
		label: "round_end",
		kind: kind,
		detail: detail,
	},
	{
		label: "game_message",
		kind: kind,
		detail: detail,
	},
	{
		label: "break_breakable",
		kind: kind,
		detail: detail,
	},
	{
		label: "break_prop",
		kind: kind,
		detail: detail,
	},
	{
		label: "entity_killed",
		kind: kind,
		detail: detail,
	},
	{
		label: "bonus_updated",
		kind: kind,
		detail: detail,
	},
	{
		label: "achievement_event",
		kind: kind,
		detail: detail,
	},
	{
		label: "achievement_increment",
		kind: kind,
		detail: detail,
	},
	{
		label: "physgun_pickup",
		kind: kind,
		detail: detail,
	},
	{
		label: "flare_ignite_npc",
		kind: kind,
		detail: detail,
	},
	{
		label: "helicopter_grenade_punt_miss",
		kind: kind,
		detail: detail,
	},
	{
		label: "user_data_downloaded",
		kind: kind,
		detail: detail,
	},
	{
		label: "ragdoll_dissolved",
		kind: kind,
		detail: detail,
	},
	{
		label: "hltv_changed_mode",
		kind: kind,
		detail: detail,
	},
	{
		label: "hltv_changed_target",
		kind: kind,
		detail: detail,
	},
	{
		label: "vote_ended",
		kind: kind,
		detail: detail,
	},
	{
		label: "vote_started",
		kind: kind,
		detail: detail,
	},
	{
		label: "vote_changed",
		kind: kind,
		detail: detail,
	},
	{
		label: "vote_passed",
		kind: kind,
		detail: detail,
	},
	{
		label: "vote_failed",
		kind: kind,
		detail: detail,
	},
	{
		label: "vote_cast",
		kind: kind,
		detail: detail,
	},
	{
		label: "vote_options",
		kind: kind,
		detail: detail,
	},
	{
		label: "replay_saved",
		kind: kind,
		detail: detail,
	},
	{
		label: "entered_performance_mode",
		kind: kind,
		detail: detail,
	},
	{
		label: "browse_replays",
		kind: kind,
		detail: detail,
	},
	{
		label: "replay_youtube_stats",
		kind: kind,
		detail: detail,
	},
	{
		label: "inventory_updated",
		kind: kind,
		detail: detail,
	},
	{
		label: "cart_updated",
		kind: kind,
		detail: detail,
	},
	{
		label: "store_pricesheet_updated",
		kind: kind,
		detail: detail,
	},
	{
		label: "gc_connected",
		kind: kind,
		detail: detail,
	},
	{
		label: "item_schema_initialized",
		kind: kind,
		detail: detail,
	}
]
Example #28
Source File: element-completion-item-povider.ts    From element-ui-helper with MIT License 4 votes vote down vote up
export class ElementCompletionItemProvider implements CompletionItemProvider<CompletionItem> {
  private _document!: TextDocument
  private _position!: Position
  private token!: CancellationToken
  private tagReg: RegExp = /<([\w-]+)\s*/g
  private attrReg: RegExp = /(?:\(|\s*)([\w-]+)=['"][^'"]*/
  private tagStartReg: RegExp = /<([\w-]*)$/
  private pugTagStartReg: RegExp = /^\s*[\w-]*$/
  private size!: number
  private quotes!: string

  /**
   * 获取前置标签
   */
  getPreTag(): TagObject | undefined {
    let line = this._position.line
    let tag: TagObject | string | undefined
    let txt = this.getTextBeforePosition(this._position)

    while (this._position.line - line < 10 && line >= 0) {
      if (line !== this._position.line) {
        txt = this._document.lineAt(line).text
      }
      tag = this.matchTag(this.tagReg, txt, line)
      if (tag === 'break') {
        return undefined
      }
      if (tag) {
        return <TagObject>tag
      }
      line--
    }
    return undefined
  }

  /**
   * 获取前置属性
   */
  getPreAttr(): string {
    let txt = this.getTextBeforePosition(this._position).replace(/"[^'"]*(\s*)[^'"]*$/, '')
    let end = this._position.character
    let start = txt.lastIndexOf(' ', end) + 1
    let parsedTxt = this._document.getText(new Range(this._position.line, start, this._position.line, end))
    return this.matchAttr(this.attrReg, parsedTxt)
  }

  /**
   * 匹配属性
   *
   * @param reg 匹配模式
   * @param txt 匹配文本
   */
  matchAttr(reg: RegExp, txt: string): string {
    let match: RegExpExecArray | null
    match = reg.exec(txt)
    if (!/"[^"]*"/.test(txt) && match) {
      return match[1]
    }
    return ''
  }

  /**
   * 匹配标签
   * @param reg 匹配模式
   * @param txt 匹配文本
   * @param line 当前行
   */
  matchTag(reg: RegExp, txt: string, line: number): TagObject | string | undefined {
    let match: RegExpExecArray | null
    let arr: TagObject[] = []

    if (/<\/?[-\w]+[^<>]*>[\s\w]*<?\s*[\w-]*$/.test(txt) || (this._position.line === line && (/^\s*[^<]+\s*>[^</>]*$/.test(txt) || /[^<>]*<$/.test(txt[txt.length - 1])))) {
      return 'break'
    }
    while ((match = reg.exec(txt))) {
      arr.push({
        text: match[1],
        offset: this._document.offsetAt(new Position(line, match.index))
      })
    }
    return arr.pop()
  }

  /**
   * 获取当前位置之前的字符串
   *
   * @param position 位置
   */
  getTextBeforePosition(position: Position): string {
    var start = new Position(position.line, 0)
    var range = new Range(start, position)
    return this._document.getText(range)
  }

  /**
   * 是否位属性值的开始
   * @param tag 标签
   * @param attr 属性
   */
  isAttrValueStart(tag: Object | undefined, attr: string) {
    return !!tag && !!attr
  }

  /**
   * 是否位属性的开始
   * @param tag 标签
   */
  isAttrStart(tag: TagObject | undefined) {
    const preText = this.getTextBeforePosition(this._position)
    return tag && / :?[\w-]*$/.test(preText)
  }

  /**
   * 是否为方法的开始
   * @param tag 标签
   */
  isEventStart(tag: TagObject | undefined) {
    const preText = this.getTextBeforePosition(this._position)
    return tag && / @[\w-]*$/.test(preText)
  }

  /**
   * 获取属性值
   *
   * @param tag 标签
   * @param attr 属性
   */
  getAttrValues(tag: string, attr: string): string[] {
    const config = workspace.getConfiguration().get<ExtensionConfigutation>('element-ui-helper')
    const language = config?.language || ExtensionLanguage.cn
    const document: Record<string, any> = localDocument[language]
    const attributes: DocumentAttribute[] = document[tag].attributes || []
    const attribute: DocumentAttribute | undefined = attributes.find((attribute) => attribute.name === attr)
    if (!attribute) {
      return []
    }
    const values = attribute.value.split(/[,/\\]/).map((item) => item.trim())
    return values
  }

  /**
   * 获取属性值的提示信息
   *
   * @param tag 标签
   * @param attr 属性
   */
  getAttrValueCompletionItems(tag: string, attr: string): CompletionItem[] {
    let completionItems: CompletionItem[] = []
    const values = this.getAttrValues(tag, attr)
    values.forEach((value) => {
      if (/\w+/.test(value)) {
        completionItems.push({
          label: `${value}`,
          sortText: `0${value}`,
          detail: `${tag}-${attr}`,
          kind: CompletionItemKind.Value,
          insertText: value
        })
      }
    })
    return completionItems
  }

  /**
   * 获取事件名称提示
   *
   * @param tag 标签
   */
  getEventCompletionItems(tag: string): CompletionItem[] {
    let completionItems: CompletionItem[] = []
    const config = workspace.getConfiguration().get<ExtensionConfigutation>('element-ui-helper')
    const language = config?.language || ExtensionLanguage.cn
    const document: Record<string, any> = localDocument[language]
    const preText = this.getTextBeforePosition(this._position)
    const prefix = preText.replace(/.*@([\w-]*)$/, '$1')
    const events: DocumentEvent[] = document[tag]?.events || []
    const likeTag = events.filter((evnet: DocumentEvent) => evnet.name.includes(prefix))
    likeTag.forEach((event: DocumentEvent) => {
      const start = preText.lastIndexOf('@') + 1
      const end = start + prefix.length
      const startPos = new Position(this._position.line, start)
      const endPos = new Position(this._position.line, end)
      const range = new Range(startPos, endPos)
      completionItems.push({
        label: `${event.name}`,
        sortText: `0${event.name}`,
        detail: `${tag} Event`,
        documentation: event.description,
        kind: CompletionItemKind.Value,
        insertText: event.name,
        range
      })
    })
    return completionItems
  }

  /**
   * 获取属性的提示信息
   *
   * @param tag 标签
   */
  getAttrCompletionItems(tag: string): CompletionItem[] {
    let completionItems: CompletionItem[] = []
    const config = workspace.getConfiguration().get<ExtensionConfigutation>('element-ui-helper')
    const language = config?.language || ExtensionLanguage.cn
    const document: Record<string, any> = localDocument[language]
    const preText = this.getTextBeforePosition(this._position)
    const prefix = preText.replace(/.*[\s@:]/g, '')
    const attributes: DocumentAttribute[] = document[tag].attributes || []
    const likeTag = attributes.filter((attribute: DocumentAttribute) => attribute.name.includes(prefix))
    likeTag.forEach((attribute: DocumentAttribute) => {
      const start = Math.max(preText.lastIndexOf(' '), preText.lastIndexOf(':')) + 1
      const end = start + prefix.length
      const startPos = new Position(this._position.line, start)
      const endPos = new Position(this._position.line, end)
      const range = new Range(startPos, endPos)
      completionItems.push({
        label: `${attribute.name}`,
        sortText: `0${attribute.name}`,
        detail: `${tag} Attribute`,
        documentation: attribute.description,
        kind: CompletionItemKind.Value,
        insertText: attribute.name,
        range
      })
    })
    return completionItems
  }

  /**
   * 是否位标签的开始
   */
  isTagStart(): boolean {
    let txt = this.getTextBeforePosition(this._position)
    return this.tagStartReg.test(txt)
  }

  /**
   * 获取标签提示
   */
  getTagCompletionItems(tag: string): CompletionItem[] {
    let completionItems: CompletionItem[] = []
    const config = workspace.getConfiguration().get<ExtensionConfigutation>('element-ui-helper')
    const language = config?.language || ExtensionLanguage.cn
    const preText = this.getTextBeforePosition(this._position)
    const document: Record<string, any> = localDocument[language]
    Object.keys(document).forEach((key) => {
      const start = preText.lastIndexOf('<') + 1
      const end = preText.length - start + 1
      const startPos = new Position(this._position.line, start)
      const endPos = new Position(this._position.line, end)
      const range = new Range(startPos, endPos)
      completionItems.push({
        label: `${key}`,
        sortText: `0${key}`,
        detail: 'ElementUI Tag',
        kind: CompletionItemKind.Value,
        insertText: new SnippetString().appendText(`${key}`).appendTabstop().appendText('>').appendTabstop().appendText(`</${key}>`),
        range
      })
    })
    return completionItems
  }

  /**
   * 提供自动完成提示
   *
   * @param document 文档
   * @param position 位置
   * @param token token
   * @param context 上下文
   */
  provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): ProviderResult<CompletionItem[] | CompletionList<CompletionItem>> {
    this._document = document
    this._position = position
    this.token = token

    let tag: TagObject | undefined = this.getPreTag()
    let attr = this.getPreAttr()

    if (!tag || !/^[E|e]l/.test(tag.text || '')) {
      // 如果不是element的标签(E|el开头) 则返回 null 表示没有hover
      return null
    } else if (this.isAttrValueStart(tag, attr)) {
      // 如果是属性值的开始
      return this.getAttrValueCompletionItems(tag.text, attr)
    } else if (this.isEventStart(tag)) {
      // 优先判定事件
      return this.getEventCompletionItems(tag.text)
    } else if (this.isAttrStart(tag)) {
      // 判断属性
      return this.getAttrCompletionItems(tag.text)
    } else if (this.isTagStart()) {
      // 判断标签
      return this.getTagCompletionItems(tag.text)
    }

    return null
  }
}