vscode#Selection TypeScript Examples

The following examples show how to use vscode#Selection. 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: utils.spec.ts    From memo with MIT License 7 votes vote down vote up
describe('getRefUriUnderCursor()', () => {
  beforeEach(closeEditorsAndCleanWorkspace);

  afterEach(closeEditorsAndCleanWorkspace);

  it('should return reference uri under cursor', async () => {
    const name0 = rndName();
    const name1 = rndName();

    await createFile(`${name0}.md`);
    await createFile(`${name1}.md`, `[[${name0}]]`);

    const doc = await openTextDocument(`${name1}.md`);
    const editor = await window.showTextDocument(doc);

    editor.selection = new Selection(0, 2, 0, 2);

    expect(getRefUriUnderCursor()!.fsPath).toContain(`${name0}.md`);
  });

  it('should not return reference uri under cursor', async () => {
    const name0 = rndName();
    const name1 = rndName();

    await createFile(`${name0}.md`);
    await createFile(`${name1}.md`, `  [[${name0}]]`);

    const doc = await openTextDocument(`${name1}.md`);
    const editor = await window.showTextDocument(doc);

    editor.selection = new Selection(0, 0, 0, 0);

    expect(getRefUriUnderCursor()).toBeNull();
  });
});
Example #2
Source File: WorkspaceWatcher.ts    From dendron with GNU Affero General Public License v3.0 6 votes vote down vote up
static moveCursorPastFrontmatter(editor: TextEditor) {
    const ctx = "moveCursorPastFrontmatter";
    const nodePosition = RemarkUtils.getNodePositionPastFrontmatter(
      editor.document.getText()
    );
    const startFsPath = editor.document.uri.fsPath;
    if (!_.isUndefined(nodePosition)) {
      const position = VSCodeUtils.point2VSCodePosition(nodePosition.end, {
        line: 1,
      });
      // If the user opened the document with something like the search window,
      // then VSCode is supposed to move the cursor to where the match is.
      // But if we move the cursor here, then it overwrites VSCode's move.
      // Worse, when VSCode calls this function the cursor hasn't updated yet
      // so the location will still be 0, so we have to delay a bit to let it update first.
      Wrap.setTimeout(() => {
        // Since we delayed, a new document could have opened. Make sure we're still in the document we expect
        if (
          VSCodeUtils.getActiveTextEditor()?.document.uri.fsPath === startFsPath
        ) {
          const { line, character } = editor.selection.active;
          // Move the cursor, but only if it hasn't already been moved by VSCode, another extension, or a very quick user
          if (line === 0 && character === 0) {
            editor.selection = new Selection(position, position);
          } else {
            Logger.debug({
              ctx,
              msg: "not moving cursor because the cursor was moved before we could get to it",
            });
          }
        } else {
          Logger.debug({
            ctx,
            msg: "not moving cursor because the document changed before we could move it",
          });
        }
      }, MOVE_CURSOR_PAST_FRONTMATTER_DELAY);
    }
  }
Example #3
Source File: documentActions.ts    From vscode-todo-md with MIT License 6 votes vote down vote up
/**
 * Reveal the line/task in the file.
 *
 * Move cursor, reveal range, highlight the line for a moment
 */
export async function revealTask(lineNumber: number, document?: TextDocument) {
	const documentToReveal = document ?? await getActiveOrDefaultDocument();
	const editor = await window.showTextDocument(documentToReveal);
	const range = new Range(lineNumber, 0, lineNumber, 0);
	editor.selection = new Selection(range.start, range.end);
	editor.revealRange(range, TextEditorRevealType.Default);
	// Highlight for a short time revealed range
	const lineHighlightDecorationType = window.createTextEditorDecorationType({
		backgroundColor: '#ffa30468',
		isWholeLine: true,
	});
	editor.setDecorations(lineHighlightDecorationType, [range]);
	setTimeout(() => {
		editor.setDecorations(lineHighlightDecorationType, []);
	}, 700);
}
Example #4
Source File: goto-definition.ts    From al-objid with MIT License 6 votes vote down vote up
async function goToManifest({ goto }: GoToDefinitionCommandContext<NinjaALRange>) {
    const { uri } = goto.app.manifest;
    const idRanges = await getIdRanges(uri);
    if (!idRanges) {
        return;
    }

    const editor = await window.showTextDocument(uri);

    const from = `${goto.range?.from}`;
    const to = `${goto.range?.to}`;

    switch (goto.type) {
        case GoToDefinitionType.IdRanges:
            editor.selection = new Selection(idRanges!.range.start, idRanges!.range.end);
            break;

        case GoToDefinitionType.Range:
            const range = idRanges.children.find(
                c =>
                    c.children.find(c => c.name === "from" && c.detail === from) &&
                    c.children.find(c => c.name === "to" && c.detail === to)
            );
            if (range) {
                editor.selection = new Selection(range.range.start, range.range.end);
            }
            break;
    }
}
Example #5
Source File: codeActionProvider.ts    From dendron with GNU Affero General Public License v3.0 6 votes vote down vote up
doctorFrontmatterProvider: CodeActionProvider = {
  provideCodeActions: sentryReportingCallback(
    (
      _document: TextDocument,
      _range: Range | Selection,
      context: CodeActionContext,
      _token: CancellationToken
    ) => {
      // No-op if we're not in a Dendron Workspace
      if (!DendronExtension.isActive()) {
        return;
      }
      // Only provide fix frontmatter action if the diagnostic is correct
      const diagnostics = context.diagnostics.filter(
        (item) => item.code === BAD_FRONTMATTER_CODE
      );
      if (diagnostics.length !== 0) {
        const action: CodeAction = {
          title: "Fix the frontmatter",
          diagnostics,
          isPreferred: true,
          kind: CodeActionKind.QuickFix,
          command: {
            command: new DoctorCommand(ExtensionProvider.getExtension()).key,
            title: "Fix the frontmatter",
            arguments: [
              { scope: "file", action: DoctorActionsEnum.FIX_FRONTMATTER },
            ],
          },
        };
        return [action];
      }
      return undefined;
    }
  ),
}
Example #6
Source File: InsertNoteCommand.ts    From dendron with GNU Affero General Public License v3.0 6 votes vote down vote up
async execute(opts: CommandOpts) {
    const ctx = "InsertNoteCommand";
    opts = _.defaults(opts, { closeAndOpenFile: true });
    Logger.info({ ctx, opts });
    const templates = opts.picks.map((pick) => {
      return pick.body;
    });
    const txt = templates.join("\n");
    const snippet = new SnippetString(txt);
    const editor = VSCodeUtils.getActiveTextEditor()!;
    const pos = editor.selection.active;
    const selection = new Selection(pos, pos);
    await editor.insertSnippet(snippet, selection);
    AnalyticsUtils.track(ExtensionEvents.DeprecationNoticeShow, {
      source: DENDRON_COMMANDS.INSERT_NOTE.key,
    });
    window
      .showWarningMessage(
        "Heads up that InsertNote is being deprecated and will be replaced with the 'Apply Template' command",
        "See whats changed"
      )
      .then((resp) => {
        if (resp === "See whats changed") {
          AnalyticsUtils.track(ExtensionEvents.DeprecationNoticeAccept, {
            source: DENDRON_COMMANDS.INSERT_NOTE.key,
          });
          VSCodeUtils.openLink(
            "https://wiki.dendron.so/notes/ftohqknticu6bw4cfmzskq6"
          );
        }
      });
    return txt;
  }
Example #7
Source File: CopyNoteRef.ts    From dendron with GNU Affero General Public License v3.0 6 votes vote down vote up
hasNextHeader(opts: { selection: Selection }) {
    const { selection } = opts;
    const lineEndForSelection = selection.end.line;
    const editor = VSCodeUtils.getActiveTextEditor() as TextEditor;
    const lineEndForDoc = editor.document.lineCount;
    const text = editor.document.getText(
      new Range(
        new Position(lineEndForSelection + 1, 0),
        new Position(lineEndForDoc, 0)
      )
    );
    return !_.isNull(text.match(/^#+\s/m));
  }
Example #8
Source File: ConvertCandidateLink.ts    From dendron with GNU Affero General Public License v3.0 6 votes vote down vote up
async execute(_opts: CommandOpts) {
    const { location, text } = _opts;
    await commands.executeCommand("vscode.open", location.uri);
    const editor = VSCodeUtils.getActiveTextEditor()!;
    const selection = editor.document.getText(location.range);
    const preConversionOffset = selection.indexOf(text);
    const convertedSelection = selection.replace(text, `[[${text}]]`);
    await editor.edit((editBuilder) => {
      editBuilder.replace(location.range, convertedSelection);
    });
    const postConversionSelectionRange = new Selection(
      new Position(
        location.range.start.line,
        location.range.start.character + preConversionOffset
      ),
      new Position(
        location.range.end.line,
        location.range.start.character + preConversionOffset + text.length + 4
      )
    );
    editor.selection = postConversionSelectionRange;
    return;
  }
Example #9
Source File: WSUtilsV2.ts    From dendron with GNU Affero General Public License v3.0 6 votes vote down vote up
async trySelectRevealNonNoteAnchor(
    editor: TextEditor,
    anchor: DNoteAnchorBasic
  ): Promise<void> {
    let position: Position | undefined;
    switch (anchor.type) {
      case "line":
        // Line anchors are direct line numbers from the start
        position = new Position(anchor.line - 1 /* line 1 is index 0 */, 0);
        break;
      case "block":
        // We don't parse non note files for anchors, so read the document and find where the anchor is
        position = editor?.document.positionAt(
          editor?.document.getText().indexOf(AnchorUtils.anchor2string(anchor))
        );
        break;
      default:
        // not supported for non-note files
        position = undefined;
    }
    if (position) {
      // if we did find the anchor, then select and scroll to it
      editor.selection = new Selection(position, position);
      editor.revealRange(editor.selection);
    }
  }
Example #10
Source File: editor-utils.ts    From vscode-code-review with MIT License 6 votes vote down vote up
clearSelection = (editor: TextEditor): void => {
  if (hasSelection(editor)) {
    editor.selections = [new Selection(new Position(0, 0), new Position(0, 0))];
  }
}
Example #11
Source File: new-trongate-module.command.ts    From trongate-vscode with MIT License 6 votes vote down vote up
// Helper Function to open the controller file and place curosr at good position
function openEditorAndPutCursorAtGoodPosition(
  targetDirectory,
  moduleName,
  isViewTemplate
) {
  const validatedModuleName = validateModuleName(moduleName);
  const upperModuleName = makeFirstLetterGoUpper(validatedModuleName);
  const controllerLocation = `${targetDirectory}/${validatedModuleName}/controllers/${upperModuleName}.php`;
  var setting: vscode.Uri = Uri.file(controllerLocation);
  setting = setting.fsPath;
  workspace.openTextDocument(setting).then((document) =>
    window.showTextDocument(document).then((e) => {
      e.selections =
        isViewTemplate === "no"
          ? [new Selection(new Position(0, 5), new Position(0, 5))]
          : [new Selection(new Position(0, 5), new Position(0, 5))];
    })
  );
}
Example #12
Source File: createSimilarTask.ts    From vscode-todo-md with MIT License 6 votes vote down vote up
export async function createSimilarTask(editor: TextEditor) {
	// Create a task with all the tags, projects and contexts of another task
	const selection = editor.selection;
	const task = getTaskAtLineExtension(selection.start.line);
	if (!task) {
		return;
	}
	const line = editor.document.lineAt(task.lineNumber);
	const edit = new WorkspaceEdit();

	const tagsAsString = task.tags.map(tag => ` #${tag}`).join('');
	const projectsAsString = task.projects.map(project => `+${project}`).join(' ');
	const contextsAsString = task.contexts.map(context => `@${context}`).join(' ');
	let newTaskAsString = tagsAsString;
	newTaskAsString += projectsAsString ? ` ${projectsAsString}` : '';
	newTaskAsString += contextsAsString ? ` ${contextsAsString}` : '';
	edit.insert(editor.document.uri, new Position(line.rangeIncludingLineBreak.end.line, line.rangeIncludingLineBreak.end.character), `${newTaskAsString}\n`);

	await applyEdit(edit, editor.document);

	editor.selection = new Selection(line.lineNumber + 1, 0, line.lineNumber + 1, 0);
}
Example #13
Source File: infoview.ts    From vscode-lean4 with Apache License 2.0 6 votes vote down vote up
private async revealEditorSelection(uri: Uri, selection?: Range) {
        let editor: TextEditor | undefined;
        for (const e of window.visibleTextEditors) {
            if (e.document.uri.toString() === uri.toString()) {
                editor = e;
                break;
            }
        }
        if (!editor) {
            const c = window.activeTextEditor ? window.activeTextEditor.viewColumn : ViewColumn.One;
            editor = await window.showTextDocument(uri, { viewColumn: c, preserveFocus: false });
        }
        if (selection !== undefined) {
            editor.revealRange(selection, TextEditorRevealType.InCenterIfOutsideViewport);
            editor.selection = new Selection(selection.start, selection.end);
            // ensure the text document has the keyboard focus.
            await window.showTextDocument(editor.document, { viewColumn: editor.viewColumn, preserveFocus: false });
        }
    }
Example #14
Source File: ALDocumentationQuickFix.ts    From vscode-alxmldocumentation with MIT License 5 votes vote down vote up
public provideCodeActions(document: TextDocument, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult<(CodeAction | Command)[]> {
        this.QuickFixActions = [];

        return new Promise(resolve => {
            resolve(this.ProvideCodeActionsAsync(document, range, context, token));
         });
    }
Example #15
Source File: md.ts    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
getURLAt = (editor: vscode.TextEditor | undefined): string => {
  if (editor) {
    const docText = editor.document.getText();
    const offsetStart = editor.document.offsetAt(editor.selection.start);
    const offsetEnd = editor.document.offsetAt(editor.selection.end);
    const selectedText = docText.substring(offsetStart, offsetEnd);
    const selectUri = true;
    const validUriChars = "A-Za-z0-9-._~:/?#@!$&'*+,;%=\\\\";
    const invalidUriChars = ["[^", validUriChars, "]"].join("");
    const regex = new RegExp(invalidUriChars);

    if (selectedText !== "" && regex.test(selectedText)) {
      return "";
    }

    const leftSplit = docText.substring(0, offsetStart).split(regex);
    const leftText = leftSplit[leftSplit.length - 1];
    const selectStart = offsetStart - leftText.length;

    const rightSplit = docText.substring(offsetEnd, docText.length);
    const rightText = rightSplit.substring(0, regex.exec(rightSplit)?.index);
    const selectEnd = offsetEnd + rightText.length;

    if (selectEnd && selectStart) {
      if (
        selectStart >= 0 &&
        selectStart < selectEnd &&
        selectEnd <= docText.length
      ) {
        if (selectUri) {
          editor.selection = new Selection(
            editor.document.positionAt(selectStart),
            editor.document.positionAt(selectEnd)
          );
          editor.revealRange(editor.selection);
        }
        return [leftText, selectedText, rightText].join("");
      }
    }
  }
  return "";
}
Example #16
Source File: editor.ts    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
/**
 * Utility method to check if the selected text is a broken wikilink
 */
export async function isBrokenWikilink({
  editor,
  selection,
  note,
  engine,
}: {
  editor: TextEditor;
  selection: vscode.Selection;
  note: NoteProps;
  engine: DEngineClient;
}): Promise<boolean> {
  const line = editor.document.lineAt(selection.start.line).text;
  const proc = MDUtilsV5.procRemarkParse(
    { mode: ProcMode.FULL },
    {
      dest: DendronASTDest.MD_DENDRON,
      engine,
      vault: note.vault,
      fname: note.fname,
    }
  );
  const parsedLine = proc.parse(line);
  let link: WikiLinkNoteV4 | UserTag | HashTag | undefined;
  let type: DECORATION_TYPES | undefined;
  let fname: string;
  await MdastUtils.visitAsync(
    parsedLine,
    [
      DendronASTTypes.WIKI_LINK,
      DendronASTTypes.USERTAG,
      DendronASTTypes.HASHTAG,
    ],
    async (linkvalue) => {
      link = linkvalue as WikiLinkNoteV4 | UserTag | HashTag;
      if (!link) return false;
      fname = link.type === DendronASTTypes.WIKI_LINK ? link.value : link.fname;
      type = (await linkedNoteType({ fname, engine })).type;
      return false;
    }
  );
  return type === DECORATION_TYPES.brokenWikilink;
}
Example #17
Source File: editor.ts    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
export async function getSelectionAnchors(opts: {
  editor: TextEditor;
  selection?: Selection;
  doStartAnchor?: boolean;
  doEndAnchor?: boolean;
  engine: DEngineClient;
}): Promise<{ startAnchor?: string; endAnchor?: string }> {
  const { editor, selection, doStartAnchor, doEndAnchor, engine } = _.defaults(
    opts,
    { doStartAnchor: true, doEndAnchor: true }
  );
  if (_.isUndefined(selection)) return {};
  const { start, end } = selection;

  // first check if there's an existing anchor
  let startAnchor = doStartAnchor
    ? getAnchorAt({ editor, position: start, engine })
    : undefined;

  // does the user have only a single
  const singleLine =
    // single line selected
    start.line === end.line ||
    // the first line selected in full, nothing on second line (default behavior when double clicking on a line)
    (start.line + 1 === end.line && end.character === 0);
  // does the user have any amount of text selected?
  const hasSelectedRegion =
    start.line !== end.line || start.character !== end.character;

  // first check if there's an existing anchor
  let endAnchor: string | undefined;
  if (!singleLine && doEndAnchor)
    endAnchor = getAnchorAt({ editor, position: end, engine });

  // if we found both anchors already, just return them.
  if (!_.isUndefined(startAnchor) && !_.isUndefined(endAnchor))
    return { startAnchor, endAnchor };

  // otherwise, we'll need to edit the document to insert block anchors
  await editor.edit((editBuilder) => {
    if (_.isUndefined(startAnchor) && doStartAnchor && hasSelectedRegion)
      startAnchor = addOrGetAnchorAt({
        editBuilder,
        editor,
        position: start,
        engine,
      });
    if (_.isUndefined(endAnchor) && doEndAnchor && !singleLine)
      endAnchor = addOrGetAnchorAt({
        editBuilder,
        editor,
        position: end,
        engine,
      });
  });
  return { startAnchor, endAnchor };
}
Example #18
Source File: infoview.ts    From vscode-lean4 with Apache License 2.0 5 votes vote down vote up
private async handleInsertText(text: string, kind: TextInsertKind, uri?: Uri, pos?: Position) {
        let editor: TextEditor | undefined
        if (uri) {
           editor = window.visibleTextEditors.find(e => e.document.uri === uri);
        } else {
            editor = window.activeTextEditor;
            if (!editor) { // sometimes activeTextEditor is null.
                editor = window.visibleTextEditors.find(e => e.document.languageId === 'lean4');
            }
        }
        if (!editor) {
            // user must have switch away from any lean source file in which case we don't know
            // what to do here.  TODO: show a popup error?  Or should we use the last uri used in
            // sendPosition and automatically activate that editor?
            return;
        }
        pos = pos ? pos : editor.selection.active;
        if (kind === 'above') {
            // in this case, assume that we actually want to insert at the same
            // indentation level as the neighboring text
            const current_line = editor.document.lineAt(pos.line);
            const spaces = current_line.firstNonWhitespaceCharacterIndex;
            const margin_str = [...Array(spaces).keys()].map(x => ' ').join('');
            let new_command = text.replace(/\n/g, '\n' + margin_str);
            new_command = `${margin_str}${new_command}\n`;
            const insertPosition = current_line.range.start;

            await editor.edit((builder) => {
                builder.insert(insertPosition, new_command);
            });

        } else {
            await editor.edit((builder) => {
                if (pos) builder.insert(pos, text);
            });
            editor.selection = new Selection(pos, pos)
        }
        // ensure the text document has the keyboard focus.
        await window.showTextDocument(editor.document, { viewColumn: editor.viewColumn, preserveFocus: false });
    }
Example #19
Source File: openReferenceInDefaultApp.spec.ts    From memo with MIT License 5 votes vote down vote up
describe('openReferenceInDefaultApp command', () => {
  beforeEach(async () => {
    await closeEditorsAndCleanWorkspace();
    (open as unknown as jest.Mock).mockClear();
  });

  afterEach(async () => {
    await closeEditorsAndCleanWorkspace();
    (open as unknown as jest.Mock).mockClear();
  });

  it('should call open command-line tool when editor selection is within the reference', async () => {
    const name0 = rndName();
    const name1 = rndName();

    await createFile(`${name0}.md`);
    await createFile(`${name1}.md`, `[[${name0}]]`);

    const doc = await openTextDocument(`${name1}.md`);
    const editor = await window.showTextDocument(doc);

    editor.selection = new Selection(0, 2, 0, 2);

    await openReferenceInDefaultApp();

    expect(open).toHaveBeenCalledWith(path.join(getWorkspaceFolder()!, `${name0}.md`));
  });

  it('should call open command-line tool when editor selection is outside of the reference', async () => {
    const name0 = rndName();
    const name1 = rndName();

    await createFile(`${name0}.md`);
    await createFile(`${name1}.md`, `  [[${name0}]]`);

    const doc = await openTextDocument(`${name1}.md`);
    const editor = await window.showTextDocument(doc);

    editor.selection = new Selection(0, 0, 0, 0);

    await openReferenceInDefaultApp();

    expect(open).not.toBeCalled();
  });
});
Example #20
Source File: testUtilsv2.ts    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
static getPresetWikiLinkSelection = (opts?: {
    line?: number;
    char?: number;
  }) =>
    new Selection(
      LocationTestUtils.getPresetWikiLinkPosition(opts),
      LocationTestUtils.getPresetWikiLinkPosition(opts)
    );
Example #21
Source File: PasteFile.ts    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
async execute(opts: CommandOpts) {
    const { filePath } = opts;

    const editor = VSCodeUtils.getActiveTextEditor();
    if (!editor) {
      const error = DendronError.createFromStatus({
        status: ERROR_STATUS.INVALID_STATE,
        message: "no active editor",
      });
      Logger.error({ error });
      return { error };
    }

    const uri = editor.document.uri;
    const ext = ExtensionProvider.getExtension();
    const { vaults, wsRoot } = ext.getDWorkspace();
    if (
      !WorkspaceUtils.isPathInWorkspace({ vaults, wsRoot, fpath: uri.fsPath })
    ) {
      const error = DendronError.createFromStatus({
        status: ERROR_STATUS.INVALID_STATE,
        message: "not in a vault",
      });
      Logger.error({ error });
      return { error };
    }
    const vault = VaultUtils.getVaultByFilePath({
      vaults,
      wsRoot,
      fsPath: uri.fsPath,
    });
    const vpath = vault2Path({ vault, wsRoot });
    const suffix = path.join("assets", cleanFname(path.basename(filePath)));
    const dstPath = path.join(vpath, suffix);

    if (!fs.existsSync(filePath)) {
      const error = DendronError.createFromStatus({
        status: ERROR_STATUS.INVALID_STATE,
        message: `${filePath} does not exist`,
      });
      Logger.error({ error });
      return { error };
    }

    if (fs.existsSync(dstPath)) {
      const error = DendronError.createFromStatus({
        status: ERROR_STATUS.INVALID_STATE,
        message: `${dstPath} already exists`,
      });
      Logger.error({ error });
      return { error };
    }

    fs.ensureDirSync(path.dirname(dstPath));
    fs.copyFileSync(filePath, dstPath);
    window.showInformationMessage(`${filePath} moved to ${dstPath}`);

    const pos = editor.selection.active;
    await editor.edit((builder) => {
      const txt = `[${path.basename(dstPath)}](${suffix})`;
      const selection = new Selection(pos, pos);
      builder.replace(selection, txt);
    });
    return {
      fpath: dstPath,
    };
  }
Example #22
Source File: VSCodeApi.ts    From vscode-alxmldocumentation with MIT License 5 votes vote down vote up
public GetSelectionByPosition(anchor: Position, active: Position): Selection {
        return new Selection(anchor, active);
    }
Example #23
Source File: CopyNoteURL.ts    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
isHeader(text: string, selection: Selection) {
    return text.startsWith("#") && selection.start.line === selection.end.line;
  }
Example #24
Source File: editor-utils.test.ts    From vscode-code-review with MIT License 5 votes vote down vote up
suite('Editor Utils', () => {
  let editorStub: TextEditor;
  let lastSetDecorationConf: TextEditorDecorationType | undefined;
  let lastSetDecorationSelection: Selection[] | undefined;
  beforeEach(() => {
    editorStub = ({
      selections: [new Selection(new Position(0, 1), new Position(2, 6))],
      setDecorations: (conf: TextEditorDecorationType, selection: Selection[]) => {
        lastSetDecorationConf = conf;
        lastSetDecorationSelection = selection;
      },
    } as unknown) as TextEditor;
  });

  afterEach(() => {
    lastSetDecorationConf = undefined;
    lastSetDecorationSelection = undefined;
  });

  suite('clearSelection', () => {
    test('should mutate selection and reset everything to 0', () => {
      clearSelection(editorStub);
      assert.deepStrictEqual(editorStub.selections, [new Selection(new Position(0, 0), new Position(0, 0))]);
    });
  });

  suite('hasSelection', () => {
    test('should detect an active selection', () => {
      const result = hasSelection(editorStub);
      assert.ok(result);
    });
    test('should detect no active selection when no positions are set', () => {
      editorStub.selections = [];
      const result = hasSelection(editorStub);
      assert.strictEqual(result, false);
    });
    test('should detect no active selection when selection positions (start, end) are equal', () => {
      editorStub.selections = [new Selection(new Position(2, 5), new Position(2, 5))];
      const result = hasSelection(editorStub);
      assert.strictEqual(result, false);
    });
  });

  suite('getSelectionStringDefinition', () => {
    test('should return a string representing the current selection', () => {
      assert.strictEqual(getSelectionStringDefinition(editorStub), '1:1-3:6');
    });
    test('should return an empty string representing when no selection is active', () => {
      editorStub.selections = [];
      assert.strictEqual(getSelectionStringDefinition(editorStub), '');
    });
  });

  suite('getSelectionRanges', () => {
    test('should return a range for the current selection', () => {
      const ranges = getSelectionRanges(editorStub);
      assert.strictEqual(ranges.length, 1);
      assert.strictEqual(ranges[0].start.line, 0);
      assert.strictEqual(ranges[0].start.character, 1);
      assert.strictEqual(ranges[0].end.line, 2);
      assert.strictEqual(ranges[0].end.character, 6);
    });
  });

  suite('isValidColorDefinition', () => {
    test('should determine if a color value is RGBA or HEX', () => {
      // empty and invalid
      assert.strictEqual(isValidColorDefinition(''), false);
      assert.strictEqual(isValidColorDefinition('ffffff'), false);
      assert.strictEqual(isValidColorDefinition('#11'), false);
      assert.strictEqual(isValidColorDefinition('#22222'), false);
      assert.strictEqual(isValidColorDefinition('#3333333'), false);
      assert.strictEqual(isValidColorDefinition('some-other'), false);
      assert.strictEqual(isValidColorDefinition('rgb(10,20,44)'), false);
      assert.strictEqual(isValidColorDefinition('rgba(,20,44)'), false);
      assert.strictEqual(isValidColorDefinition('rgba(23)'), false);
      assert.strictEqual(isValidColorDefinition('rgba(23,34)'), false);
      assert.strictEqual(isValidColorDefinition('rgba(23,34)'), false);
      assert.strictEqual(isValidColorDefinition('rgba(10,20,44)'), false);
      assert.strictEqual(isValidColorDefinition('rgba(100,200,300,1.1)'), false);

      // hex
      assert.strictEqual(isValidColorDefinition('#fff'), true);
      assert.strictEqual(isValidColorDefinition('#FFF'), true);
      assert.strictEqual(isValidColorDefinition('#dddd'), true);
      assert.strictEqual(isValidColorDefinition('#DDDD'), true);
      assert.strictEqual(isValidColorDefinition('#ababab'), true);
      assert.strictEqual(isValidColorDefinition('#ABABAB'), true);
      assert.strictEqual(isValidColorDefinition('#ababab33'), true);
      assert.strictEqual(isValidColorDefinition('#ABABAB33'), true);

      // rgba
      assert.strictEqual(isValidColorDefinition('rgba(100,200,300,0.8)'), true);
    });
  });

  suite('themeColorForPriority', () => {
    test('should colorize the given selection with decoration', () => {
      assert.deepStrictEqual(themeColorForPriority(3), new ThemeColor('codereview.priority.red'));
      assert.deepStrictEqual(themeColorForPriority(2), new ThemeColor('codereview.priority.yellow'));
      assert.deepStrictEqual(themeColorForPriority(1), new ThemeColor('codereview.priority.green'));
      assert.strictEqual(themeColorForPriority(0), undefined);
    });
  });

  suite('symbolForPriority', () => {
    test('should colorize the given selection with decoration', () => {
      assert.strictEqual(symbolForPriority(3), '⇡');
      assert.strictEqual(symbolForPriority(2), '⇢');
      assert.strictEqual(symbolForPriority(1), '⇣');
      assert.strictEqual(symbolForPriority(0), undefined);
    });
  });
});
Example #25
Source File: decoration-utils.test.ts    From vscode-code-review with MIT License 5 votes vote down vote up
suite('Decoration Utils', () => {
  let editorStub: TextEditor;
  let lastSetDecorationConf: TextEditorDecorationType | undefined;
  let lastSetDecorationSelection!: Selection[] | DecorationOptions[] | undefined;
  beforeEach(() => {
    editorStub = ({
      selections: [new Selection(new Position(0, 1), new Position(2, 6))],
      setDecorations: (conf: TextEditorDecorationType, selection: Selection[]) => {
        lastSetDecorationConf = conf;
        lastSetDecorationSelection = selection;
      },
    } as unknown) as TextEditor;
  });

  afterEach(() => {
    lastSetDecorationConf = undefined;
    lastSetDecorationSelection = undefined;
  });

  suite('underlineDecoration', () => {
    test('should underline comments', () => {
      const contextStub = {
        asAbsolutePath: (p) => p,
      } as ExtensionContext;
      const deco = new Decorations(contextStub);

      const csvEntries = [{ lines: '1:0-3:4|9:0-11:3' } as CsvEntry, { lines: '17:3-19:2' } as CsvEntry];
      const decoration = deco.underlineDecoration(csvEntries, editorStub);
      const decorationOptions = lastSetDecorationSelection as DecorationOptions[];

      assert.ok(deco);
      assert.deepStrictEqual(lastSetDecorationConf, deco.decorationDeclarationType);

      assert.strictEqual(decorationOptions[0].range.start.line, 0);
      assert.strictEqual(decorationOptions[0].range.start.character, 0);
      assert.strictEqual(decorationOptions[0].range.end.line, 2);
      assert.strictEqual(decorationOptions[0].range.end.character, 4);

      assert.strictEqual(decorationOptions[1].range.start.line, 8);
      assert.strictEqual(decorationOptions[1].range.start.character, 0);
      assert.strictEqual(decorationOptions[1].range.end.line, 10);
      assert.strictEqual(decorationOptions[1].range.end.character, 3);

      assert.strictEqual(decorationOptions[2].range.start.line, 16);
      assert.strictEqual(decorationOptions[2].range.start.character, 3);
      assert.strictEqual(decorationOptions[2].range.end.line, 18);
      assert.strictEqual(decorationOptions[2].range.end.character, 2);
    });
  });

  suite('commentIconDecoration', () => {
    test('should underline comments', () => {
      const contextStub = {
        asAbsolutePath: (p) => p,
      } as ExtensionContext;
      const deco = new Decorations(contextStub);

      const csvEntries = [{ lines: '1:0-3:4|9:0-11:3' } as CsvEntry, { lines: '17:3-19:2' } as CsvEntry];
      const decoration = deco.commentIconDecoration(csvEntries, editorStub);
      const decorationOptions = lastSetDecorationSelection as DecorationOptions[];

      assert.ok(deco);
      assert.deepStrictEqual(lastSetDecorationConf, deco.commentDecorationType);

      assert.strictEqual(decorationOptions[0].range.start.line, 0);
      assert.strictEqual(decorationOptions[0].range.start.character, 1024);
      assert.strictEqual(decorationOptions[0].range.end.line, 0);
      assert.strictEqual(decorationOptions[0].range.end.character, 1024);

      assert.strictEqual(decorationOptions[1].range.start.line, 8);
      assert.strictEqual(decorationOptions[1].range.start.character, 1024);
      assert.strictEqual(decorationOptions[1].range.end.line, 8);
      assert.strictEqual(decorationOptions[1].range.end.character, 1024);

      assert.strictEqual(decorationOptions[2].range.start.line, 16);
      assert.strictEqual(decorationOptions[2].range.start.character, 1024);
      assert.strictEqual(decorationOptions[2].range.end.line, 16);
      assert.strictEqual(decorationOptions[2].range.end.character, 1024);
    });
  });

  suite('colorizedBackgroundDecoration', () => {
    test('should colorize the given selection with decoration', () => {
      const selections = [new Range(new Position(2, 5), new Position(2, 5))];
      const decoration = colorizedBackgroundDecoration(selections, editorStub, '#fdfdfd');
      assert.ok(decoration);
      assert.deepStrictEqual(lastSetDecorationConf, decoration);
      assert.deepStrictEqual(lastSetDecorationSelection, selections);
    });
  });
});
Example #26
Source File: formatting.ts    From twee3-language-tools with MIT License 5 votes vote down vote up
/**
 * Add or remove `startPattern`/`endPattern` according to the context
 * @param editor
 * @param options The undo/redo behavior
 * @param cursor cursor position
 * @param range range to be replaced
 * @param isSelected is this range selected
 * @param startPtn
 * @param endPtn
 */
function wrapRange(editor: TextEditor, wsEdit: WorkspaceEdit, shifts: [Position, number][], newSelections: Selection[], i: number, shift: number, cursor: Position, range: Range, isSelected: boolean, startPtn: string, endPtn: string) {
    let text = editor.document.getText(range);
    const prevSelection = newSelections[i];
    const ptnLength = (startPtn + endPtn).length;

    let newCursorPos = cursor.with({ character: cursor.character + shift });
    let newSelection: Selection;
    if (isWrapped(text, startPtn, endPtn)) {
        // remove start/end patterns from range
        wsEdit.replace(editor.document.uri, range, text.substr(startPtn.length, text.length - ptnLength));

        shifts.push([range.end, -ptnLength]);

        // Fix cursor position
        if (!isSelected) {
            if (!range.isEmpty) { // means quick styling
                if (cursor.character === range.end.character) {
                    newCursorPos = cursor.with({ character: cursor.character + shift - ptnLength });
                } else {
                    newCursorPos = cursor.with({ character: cursor.character + shift - startPtn.length });
                }
            } else { // means `**|**` -> `|`
                newCursorPos = cursor.with({ character: cursor.character + shift + startPtn.length });
            }
            newSelection = new Selection(newCursorPos, newCursorPos);
        } else {
            newSelection = new Selection(
                prevSelection.start.with({ character: prevSelection.start.character + shift }),
                prevSelection.end.with({ character: prevSelection.end.character + shift - ptnLength })
            );
        }
    } else {
        // add start/end patterns around range
        wsEdit.replace(editor.document.uri, range, startPtn + text + endPtn);

        shifts.push([range.end, ptnLength]);

        // Fix cursor position
        if (!isSelected) {
            if (!range.isEmpty) { // means quick styling
                if (cursor.character === range.end.character) {
                    newCursorPos = cursor.with({ character: cursor.character + shift + ptnLength });
                } else {
                    newCursorPos = cursor.with({ character: cursor.character + shift + startPtn.length });
                }
            } else { // means `|` -> `**|**`
                newCursorPos = cursor.with({ character: cursor.character + shift + startPtn.length });
            }
            newSelection = new Selection(newCursorPos, newCursorPos);
        } else {
            newSelection = new Selection(
                prevSelection.start.with({ character: prevSelection.start.character + shift }),
                prevSelection.end.with({ character: prevSelection.end.character + shift + ptnLength })
            );
        }
    }

    newSelections[i] = newSelection;
}
Example #27
Source File: formatting.ts    From twee3-language-tools with MIT License 5 votes vote down vote up
export function styleByWrapping(editor: TextEditor, startPattern: string, endPattern = startPattern) {
    let selections = editor.selections;

    let batchEdit = new WorkspaceEdit();
    let shifts: [Position, number][] = [];
    let newSelections: Selection[] = selections.slice();

    for (const [i, selection] of selections.entries()) {

        let cursorPos = selection.active;
        const shift = shifts.map(([pos, s]) => (selection.start.line === pos.line && selection.start.character >= pos.character) ? s : 0)
            .reduce((a, b) => a + b, 0);

        if (selection.isEmpty) {
            const context = getContext(editor, cursorPos, startPattern, endPattern);

            // Patterns are added for SugarCube
            // No selected text
            if (
                startPattern === endPattern &&
                ["**", "*", "__", "_", "//", "''"].includes(startPattern) &&
                context === `${startPattern}text|${endPattern}`
            ) {
                // `**text|**` to `**text**|`
                let newCursorPos = cursorPos.with({ character: cursorPos.character + shift + endPattern.length });
                newSelections[i] = new Selection(newCursorPos, newCursorPos);
                continue;
            } else if (context === `${startPattern}|${endPattern}`) {
                // `**|**` to `|`
                let start = cursorPos.with({ character: cursorPos.character - startPattern.length });
                let end = cursorPos.with({ character: cursorPos.character + endPattern.length });
                wrapRange(editor, batchEdit, shifts, newSelections, i, shift, cursorPos, new Range(start, end), false, startPattern, endPattern);
            } else {
                // Select word under cursor
                // The markdown extension uses a custom regex very similar to this one for their def
                // of word. This removes the exclusion of certain characters since formats use them.
                let wordRange = editor.document.getWordRangeAtPosition(cursorPos, /(-?\d*\.\d\w*)|([^\!\@\#\%\^\&\(\)\-\=\+\[\{\]\}\\\|\;\:\"\,\.\<\>\?\s\,\。\《\》\?\;\:\‘\“\’\”\(\)\【\】\、]+)/g);
                if (wordRange == undefined) {
                    wordRange = selection;
                }
                // One special case: toggle strikethrough in task list
                const currentTextLine = editor.document.lineAt(cursorPos.line);
                if (startPattern === '~~' && /^\s*[\*\+\-] (\[[ x]\] )? */g.test(currentTextLine.text)) {
                    let match = currentTextLine.text.match(/^\s*[\*\+\-] (\[[ x]\] )? */g) as RegExpMatchArray;
                    wordRange = currentTextLine.range.with(new Position(cursorPos.line, match[0].length));
                }
                wrapRange(editor, batchEdit, shifts, newSelections, i, shift, cursorPos, wordRange, false, startPattern, endPattern);
            }
        } else {
            // Text selected
            wrapRange(editor, batchEdit, shifts, newSelections, i, shift, cursorPos, selection, true, startPattern, endPattern);
        }
    }

    return workspace.applyEdit(batchEdit).then(() => {
        editor.selections = newSelections;
    });
}
Example #28
Source File: ALDocumentationQuickFix.ts    From vscode-alxmldocumentation with MIT License 5 votes vote down vote up
private async ProvideCodeActionsAsync(document: TextDocument, range: Range | Selection, context: CodeActionContext, token: CancellationToken): Promise<(CodeAction | Command)[] | null | undefined> {
        let alObject: ALObject | null = await ALSyntaxUtil.GetALObject(document);
        if (alObject === null) {
            return;
        }

        context.diagnostics.filter(diagnostics => diagnostics.source === ALXmlDocDiagnosticPrefix).forEach(diagnostic => {
            if (diagnostic === undefined) {
                return;
            }

            let diagCodes: Array<string> = [];
            if (diagnostic.code!.toString().indexOf(',') !== -1) { // multiple diagnostic codes
                diagnostic.code!.toString().split(', ').forEach(diagnosticCode => {
                    diagCodes.push(diagnosticCode);
                });
            } else { // just one diagnostic code
                diagCodes.push(diagnostic.code!.toString());
            }
            diagCodes.forEach(diagnosticCode => {
                let alProcedure: ALProcedure | undefined;
                if ((diagnosticCode !== ALXmlDocDiagnosticCode.ObjectDocumentationMissing) && (diagnosticCode !== ALXmlDocDiagnosticCode.ParameterUnnecessary)) {
                    alProcedure = alObject?.Procedures?.find(alProcedure => (alProcedure.LineNo === range.start.line));
                    if (alProcedure === undefined) {
                        console.error(`[ProvideCodeActionsAsync] - Unable to locate ALProcedure object for diagnostics entry. Please report this error at https://github.com/365businessdev/vscode-alxmldocumentation/issues`);
                        return;
                    }
                }

                switch (diagnosticCode) {
                    case ALXmlDocDiagnosticCode.DocumentationMissing:
                        this.AddDocumentationMissingCodeAction(alProcedure, diagnostic);
                        break;
                    case ALXmlDocDiagnosticCode.SummaryMissing:
                        this.AddSummaryDocumentationMissingCodeAction(alProcedure, diagnostic);
                        break;
                    case ALXmlDocDiagnosticCode.ParameterMissing:
                        this.AddParameterDocumentationMissingCodeAction(alProcedure, diagnostic);
                        break;
                    case ALXmlDocDiagnosticCode.ReturnTypeMissing:
                        this.AddReturnDocumentationMissingCodeAction(alProcedure, diagnostic);
                        break;
                    case ALXmlDocDiagnosticCode.ParameterUnnecessary:
                        this.AddUnnecessaryParameterDocumentationMissingCodeAction(alProcedure, diagnostic);
                        break;
                    case ALXmlDocDiagnosticCode.ObjectDocumentationMissing:
                        this.AddObjectDocumentationMissingCodeAction(alObject, diagnostic);
                        break;
                }
            });

        });
        
        return this.QuickFixActions;
    }
Example #29
Source File: goto-definition.ts    From al-objid with MIT License 4 votes vote down vote up
async function goToConfiguration({ goto }: GoToDefinitionCommandContext<NinjaALRange>) {
    const { uri } = goto.app.config;
    const from = `${goto.range?.from}`;
    const to = `${goto.range?.to}`;

    let selection: Selection | undefined;
    let selections: Selection[] = [];
    let idRanges: DocumentSymbol | undefined;
    let objectRanges: DocumentSymbol | undefined;
    let objectTypeRanges: DocumentSymbol | undefined;
    let logicalRanges: DocumentSymbol[] | undefined;

    switch (goto.type) {
        case GoToDefinitionType.IdRanges:
            idRanges = await getIdRanges(uri);
            selection = new Selection(idRanges!.range.start, idRanges!.range.end);
            break;

        case GoToDefinitionType.ObjectRanges:
            objectRanges = await getObjectRanges(uri);
            selection = new Selection(objectRanges!.range.start, objectRanges!.range.end);
            break;

        case GoToDefinitionType.LogicalName:
            logicalRanges = await getNamedLogicalRanges(uri, goto.logicalName!);
            if (!logicalRanges || logicalRanges.length === 0) {
                return;
            }
            for (let range of logicalRanges!) {
                selections.push(new Selection(range.range.start, range.range.end));
            }
            break;

        case GoToDefinitionType.Range:
            logicalRanges = await getNamedLogicalRanges(uri, goto.range!.description);
            if (!logicalRanges || logicalRanges.length === 0) {
                return;
            }
            const range = logicalRanges.find(
                r =>
                    r.children.find(c => c.name === "from" && c.detail === from) &&
                    r.children.find(c => c.name === "to" && c.detail === to)
            );
            if (range) {
                selection = new Selection(range.range.start, range.range.end);
            }
            break;

        case GoToDefinitionType.ObjectType:
            objectTypeRanges = await getObjectTypeRanges(uri, goto.objectType!);
            if (objectTypeRanges) {
                selection = new Selection(objectTypeRanges.range.start, objectTypeRanges.range.end);
            }
            break;

        case GoToDefinitionType.ObjectTypeRanges:
            objectTypeRanges = await getObjectTypeRanges(uri, goto.objectType!);
            const logicalObjectTypeRanges = objectTypeRanges?.children.filter(c =>
                c.children.find(c => c.name === "description" && c.detail === goto.logicalName!)
            );
            if (!logicalObjectTypeRanges) {
                return;
            }
            for (let range of logicalObjectTypeRanges) {
                selections.push(new Selection(range.range.start, range.range.end));
            }
            break;

        case GoToDefinitionType.ObjectTypeRange:
            objectTypeRanges = await getObjectTypeRanges(uri, goto.objectType!);
            const logicalObjectTypeRange = objectTypeRanges?.children.find(
                c =>
                    c.children.find(c => c.name === "description" && c.detail === goto.range!.description) &&
                    c.children.find(c => c.name === "from" && c.detail === from) &&
                    c.children.find(c => c.name === "to" && c.detail === to)
            );
            if (logicalObjectTypeRange) {
                selection = new Selection(logicalObjectTypeRange.range.start, logicalObjectTypeRange.range.end);
            }
    }

    if (!selection && selections.length === 0) {
        return;
    }

    const editor = await window.showTextDocument(uri);
    if (selection) {
        editor.selection = selection;
        editor.revealRange(new Range(selection.start, selection.end));
    } else {
        editor.selections = selections;
        const firstSelection = selections.reduce(
            (previous, current) => (current.start.line < previous.start.line ? current : previous),
            selections[0]
        );
        const lastSelection = selections.reduce(
            (previous, current) => (current.end.line > previous.end.line ? current : previous),
            selections[0]
        );
        editor.revealRange(new Range(firstSelection.start, lastSelection.end));
    }
}