-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add "Extract range to a new note" command
- Loading branch information
Showing
13 changed files
with
312 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Notes refactoring | ||
|
||
## Extract range to a new note | ||
|
||
Select the following fragment, hit `cmd/ctrl + .` and select `Extract range to a new note` [code action](https://code.visualstudio.com/docs/editor/refactoring#_code-actions-quick-fixes-and-refactorings). | ||
|
||
![[Extracting range to a new note.gif]] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import vscode, { window } from 'vscode'; | ||
import path from 'path'; | ||
|
||
import extractRangeToNewNote from './extractRangeToNewNote'; | ||
import { getWorkspaceFolder } from '../utils'; | ||
import { | ||
closeEditorsAndCleanWorkspace, | ||
rndName, | ||
createFile, | ||
openTextDocument, | ||
} from '../test/testUtils'; | ||
|
||
describe('extractRangeToNewNote command', () => { | ||
beforeEach(closeEditorsAndCleanWorkspace); | ||
|
||
afterEach(closeEditorsAndCleanWorkspace); | ||
|
||
it('should extract range to a new note', async () => { | ||
const name0 = rndName(); | ||
const name1 = rndName(); | ||
|
||
await createFile(`${name0}.md`, 'Hello world.'); | ||
|
||
const doc = await openTextDocument(`${name0}.md`); | ||
|
||
const targetPathInputBoxSpy = jest.spyOn(vscode.window, 'showInputBox'); | ||
|
||
targetPathInputBoxSpy.mockReturnValue( | ||
Promise.resolve(path.join(getWorkspaceFolder()!, `${name1}.md`)), | ||
); | ||
|
||
await extractRangeToNewNote(doc, new vscode.Range(0, 0, 0, 12)); | ||
|
||
expect(await doc.getText()).toBe(''); | ||
|
||
const newDoc = await openTextDocument(`${name1}.md`); | ||
|
||
expect(await newDoc.getText()).toBe('Hello world.'); | ||
|
||
targetPathInputBoxSpy.mockRestore(); | ||
}); | ||
|
||
it('should extract a multiline range to a new note', async () => { | ||
const name0 = rndName(); | ||
const name1 = rndName(); | ||
|
||
await createFile( | ||
`${name0}.md`, | ||
`Multiline | ||
Hello world.`, | ||
); | ||
|
||
const doc = await openTextDocument(`${name0}.md`); | ||
|
||
const targetPathInputBoxSpy = jest.spyOn(vscode.window, 'showInputBox'); | ||
|
||
targetPathInputBoxSpy.mockReturnValue( | ||
Promise.resolve(path.join(getWorkspaceFolder()!, `${name1}.md`)), | ||
); | ||
|
||
await extractRangeToNewNote(doc, new vscode.Range(0, 0, 1, 16)); | ||
|
||
expect(await doc.getText()).toBe(''); | ||
|
||
const newDoc = await openTextDocument(`${name1}.md`); | ||
|
||
expect(await newDoc.getText()).toMatchInlineSnapshot(` | ||
"Multiline | ||
Hello world." | ||
`); | ||
|
||
targetPathInputBoxSpy.mockRestore(); | ||
}); | ||
|
||
it('should extract range from active markdown file', async () => { | ||
const name0 = rndName(); | ||
const name1 = rndName(); | ||
|
||
await createFile(`${name0}.md`, 'Hello world.'); | ||
|
||
const doc = await openTextDocument(`${name0}.md`); | ||
const editor = await window.showTextDocument(doc); | ||
|
||
editor.selection = new vscode.Selection(0, 0, 0, 12); | ||
|
||
const targetPathInputBoxSpy = jest.spyOn(vscode.window, 'showInputBox'); | ||
|
||
targetPathInputBoxSpy.mockReturnValue( | ||
Promise.resolve(path.join(getWorkspaceFolder()!, `${name1}.md`)), | ||
); | ||
|
||
await extractRangeToNewNote(); | ||
|
||
expect(await doc.getText()).toBe(''); | ||
|
||
const newDoc = await openTextDocument(`${name1}.md`); | ||
|
||
expect(await newDoc.getText()).toBe('Hello world.'); | ||
|
||
targetPathInputBoxSpy.mockRestore(); | ||
}); | ||
|
||
it('should not extract anything from unknown file format', async () => { | ||
const name0 = rndName(); | ||
|
||
await createFile(`${name0}.txt`, 'Hello world.'); | ||
|
||
const doc = await openTextDocument(`${name0}.txt`); | ||
const editor = await window.showTextDocument(doc); | ||
|
||
editor.selection = new vscode.Selection(0, 0, 0, 12); | ||
|
||
const targetPathInputBoxSpy = jest.spyOn(vscode.window, 'showInputBox'); | ||
|
||
await extractRangeToNewNote(); | ||
|
||
expect(await doc.getText()).toBe('Hello world.'); | ||
|
||
expect(targetPathInputBoxSpy).not.toBeCalled(); | ||
|
||
targetPathInputBoxSpy.mockRestore(); | ||
}); | ||
|
||
it('should fail when target path is outside of the workspace', async () => { | ||
const name0 = rndName(); | ||
|
||
await createFile(`${name0}.md`, 'Hello world.'); | ||
|
||
const doc = await openTextDocument(`${name0}.md`); | ||
|
||
const targetPathInputBoxSpy = jest.spyOn(vscode.window, 'showInputBox'); | ||
|
||
targetPathInputBoxSpy.mockReturnValue(Promise.resolve('/random-path/file.md')); | ||
|
||
expect(extractRangeToNewNote(doc, new vscode.Range(0, 0, 0, 12))).rejects.toThrowError( | ||
'should be within the current workspace', | ||
); | ||
|
||
targetPathInputBoxSpy.mockRestore(); | ||
}); | ||
|
||
it('should fail when entered file already exists', async () => { | ||
const name0 = rndName(); | ||
const name1 = rndName(); | ||
|
||
await createFile(`${name0}.md`, 'Hello world.'); | ||
await createFile(`${name1}.md`); | ||
|
||
const doc = await openTextDocument(`${name0}.md`); | ||
|
||
const targetPathInputBoxSpy = jest.spyOn(vscode.window, 'showInputBox'); | ||
|
||
targetPathInputBoxSpy.mockReturnValue( | ||
Promise.resolve(path.join(getWorkspaceFolder()!, `${name1}.md`)), | ||
); | ||
|
||
expect(extractRangeToNewNote(doc, new vscode.Range(0, 0, 0, 12))).rejects.toThrowError( | ||
'Such file or directory already exists. Please use unique filename instead.', | ||
); | ||
|
||
targetPathInputBoxSpy.mockRestore(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import vscode, { Uri, window } from 'vscode'; | ||
import fs from 'fs-extra'; | ||
import path from 'path'; | ||
|
||
const filename = 'New File.md'; | ||
|
||
const prompt = 'New location within workspace'; | ||
|
||
const createFile = async (uri: vscode.Uri, content: string) => { | ||
const workspaceEdit = new vscode.WorkspaceEdit(); | ||
workspaceEdit.createFile(uri); | ||
workspaceEdit.set(uri, [new vscode.TextEdit(new vscode.Range(0, 0, 0, 0), content)]); | ||
|
||
await vscode.workspace.applyEdit(workspaceEdit); | ||
}; | ||
|
||
const showFile = async (uri: vscode.Uri) => | ||
await window.showTextDocument(await vscode.workspace.openTextDocument(uri)); | ||
|
||
const deleteRange = async (document: vscode.TextDocument, range: vscode.Range) => { | ||
const editor = await window.showTextDocument(document); | ||
await editor.edit((edit) => edit.delete(range)); | ||
}; | ||
|
||
const extractRangeToNewNote = async ( | ||
documentParam?: vscode.TextDocument, | ||
rangeParam?: vscode.Range, | ||
) => { | ||
const document = documentParam ? documentParam : window.activeTextEditor?.document; | ||
|
||
if (!document || (document && document.languageId !== 'markdown')) { | ||
return; | ||
} | ||
|
||
const range = rangeParam ? rangeParam : window.activeTextEditor?.selection; | ||
|
||
if (!range || (range && range.isEmpty)) { | ||
return; | ||
} | ||
|
||
const filepath = path.join(path.dirname(document.uri.fsPath), filename); | ||
const targetPath = await window.showInputBox({ | ||
prompt, | ||
value: filepath, | ||
valueSelection: [filepath.lastIndexOf(filename), filepath.lastIndexOf('.md')], | ||
}); | ||
|
||
const targetUri = Uri.file(targetPath || ''); | ||
|
||
const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri); | ||
|
||
if (!targetPath) { | ||
return; | ||
} | ||
|
||
if (!vscode.workspace.getWorkspaceFolder(targetUri)) { | ||
throw new Error( | ||
`New location "${targetUri.fsPath}" should be within the current workspace.${ | ||
workspaceFolder ? ` Example: ${path.join(workspaceFolder.uri.fsPath, filename)}` : '' | ||
}`, | ||
); | ||
} | ||
|
||
if (await fs.pathExists(targetUri.fsPath)) { | ||
throw new Error('Such file or directory already exists. Please use unique filename instead.'); | ||
} | ||
|
||
// Order matters | ||
await createFile(targetUri, document.getText(range).trim()); | ||
|
||
await deleteRange(document, range); | ||
|
||
await showFile(targetUri); | ||
}; | ||
|
||
export default extractRangeToNewNote; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import vscode from 'vscode'; | ||
|
||
import codeActionProvider from './codeActionProvider'; | ||
import { rndName, createFile, openTextDocument } from '../test/testUtils'; | ||
|
||
describe('codeActionProvider', () => { | ||
it('should provide code actions', async () => { | ||
const name0 = rndName(); | ||
|
||
await createFile(`${name0}.md`, 'Hello world!'); | ||
|
||
const doc = await openTextDocument(`${name0}.md`); | ||
const range = new vscode.Range(0, 0, 0, 12); | ||
|
||
expect( | ||
codeActionProvider.provideCodeActions(doc, range, undefined as any, undefined as any), | ||
).toEqual([ | ||
{ | ||
title: 'Extract range to a new note', | ||
command: 'memo.extractRangeToNewNote', | ||
arguments: [doc, range], | ||
}, | ||
]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { CodeActionProvider } from 'vscode'; | ||
|
||
const codeActionProvider: CodeActionProvider = { | ||
provideCodeActions(document, range) { | ||
if (range.isEmpty) { | ||
return []; | ||
} | ||
|
||
return [ | ||
{ | ||
title: 'Extract range to a new note', | ||
command: 'memo.extractRangeToNewNote', | ||
arguments: [document, range], | ||
}, | ||
]; | ||
}, | ||
}; | ||
|
||
export default codeActionProvider; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters