Skip to content

Commit 323c145

Browse files
authored
Merge pull request #68 from hackmdio/feature/create-with-editor-env
Create note with env EDITOR
2 parents 5e6d702 + 5959ddc commit 323c145

File tree

5 files changed

+115
-27
lines changed

5 files changed

+115
-27
lines changed

.github/workflows/config-test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99

1010
strategy:
1111
matrix:
12-
node-version: ['12', '14', '16']
12+
node-version: ['18']
1313

1414
steps:
1515
- name: Checkout repository

.nvmrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
17
1+
18

src/commands/notes/create.ts

+50-20
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1-
import {CommentPermissionType, CreateNoteOptions, NotePermissionRole} from '@hackmd/api/dist/type'
1+
import {
2+
CommentPermissionType,
3+
CreateNoteOptions,
4+
NotePermissionRole,
5+
} from '@hackmd/api/dist/type'
26
import {CliUx, Flags} from '@oclif/core'
7+
import * as fs from 'fs'
38

49
import HackMDCommand from '../../command'
5-
import {commentPermission, noteContent, notePermission, noteTitle} from '../../flags'
6-
import {safeStdinRead} from '../../utils'
10+
import {
11+
commentPermission,
12+
noteContent,
13+
notePermission,
14+
noteTitle,
15+
} from '../../flags'
16+
import openEditor from '../../open-editor'
17+
import {safeStdinRead, temporaryMD} from '../../utils'
718

8-
export default class Create extends HackMDCommand {
19+
export default class CreateCommand extends HackMDCommand {
920
static description = 'Create a note'
1021

1122
static examples = [
@@ -16,7 +27,7 @@ export default class Create extends HackMDCommand {
1627
raUuSTetT5uQbqQfLnz9lA A new note gvfz2UB5THiKABQJQnLs6Q null`,
1728

1829
'Or you can pipe content via Unix pipeline:',
19-
'cat README.md | hackmd-cli notes create'
30+
'cat README.md | hackmd-cli notes create',
2031
]
2132

2233
static flags = {
@@ -26,40 +37,59 @@ raUuSTetT5uQbqQfLnz9lA A new note gvfz2UB5THiKABQJQnLs6Q
2637
readPermission: notePermission(),
2738
writePermission: notePermission(),
2839
commentPermission: commentPermission(),
40+
editor: Flags.boolean({
41+
char: 'e',
42+
description: 'create note with $EDITOR',
43+
}),
2944
...CliUx.ux.table.flags(),
3045
}
3146

3247
async run() {
33-
const {flags} = await this.parse(Create)
48+
const {flags} = await this.parse(CreateCommand)
3449
const pipeString = safeStdinRead()
3550

3651
const options: CreateNoteOptions = {
3752
title: flags.title,
3853
content: pipeString || flags.content,
3954
readPermission: flags.readPermission as NotePermissionRole,
4055
writePermission: flags.writePermission as NotePermissionRole,
41-
commentPermission: flags.commentPermission as CommentPermissionType
56+
commentPermission: flags.commentPermission as CommentPermissionType,
57+
}
58+
59+
if (flags.editor) {
60+
try {
61+
const mdFile = temporaryMD()
62+
await openEditor(mdFile)
63+
64+
options.content = fs.readFileSync(mdFile).toString()
65+
} catch (e) {
66+
this.error(e as Error)
67+
}
4268
}
4369

4470
try {
4571
const APIClient = await this.getAPIClient()
4672
const note = await APIClient.createNote(options)
4773

48-
CliUx.ux.table([note], {
49-
id: {
50-
header: 'ID',
51-
},
52-
title: {},
53-
userPath: {
54-
header: 'User path'
74+
CliUx.ux.table(
75+
[note],
76+
{
77+
id: {
78+
header: 'ID',
79+
},
80+
title: {},
81+
userPath: {
82+
header: 'User path',
83+
},
84+
teamPath: {
85+
header: 'Team path',
86+
},
5587
},
56-
teamPath: {
57-
header: 'Team path'
88+
{
89+
printLine: this.log.bind(this),
90+
...flags,
5891
}
59-
}, {
60-
printLine: this.log.bind(this),
61-
...flags
62-
})
92+
)
6393
} catch (e) {
6494
this.log('Create note failed')
6595
this.error(e as Error)

src/open-editor.ts

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {ChildProcess, spawn} from 'child_process'
2+
3+
interface EditorOptions {
4+
editor?: string
5+
}
6+
7+
export function openEditor(
8+
file: string,
9+
opts: EditorOptions = {}
10+
): Promise<void> {
11+
return new Promise((resolve, reject) => {
12+
const editor = getEditor(opts.editor)
13+
const args = editor.split(/\s+/)
14+
const bin = args.shift()
15+
16+
if (!bin) {
17+
reject(new Error('Editor binary not found'))
18+
return
19+
}
20+
21+
const ps: ChildProcess = spawn(bin, [...args, file], {stdio: 'inherit'})
22+
23+
ps.on('exit', () => {
24+
resolve()
25+
})
26+
27+
ps.on('error', (err: Error) => {
28+
reject(err)
29+
})
30+
})
31+
}
32+
33+
function getEditor(editor?: string): string {
34+
return (
35+
editor || process.env.VISUAL || process.env.EDITOR || getDefaultEditor()
36+
)
37+
}
38+
39+
function getDefaultEditor(): string {
40+
return /^win/.test(process.platform) ? 'notepad' : 'vim'
41+
}
42+
43+
export default openEditor

src/utils.ts

+20-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import fs from 'fs'
2-
import {homedir} from 'os'
1+
import fs from 'fs-extra'
2+
import {homedir, tmpdir} from 'os'
33
import * as path from 'path'
44

55
export function getConfigFilePath() {
@@ -10,7 +10,14 @@ export function getConfigFilePath() {
1010
configDir = path.join(homedir(), '.hackmd')
1111
}
1212

13-
return path.join(configDir, 'config.json')
13+
const configPath = path.join(configDir, 'config.json')
14+
15+
if (!fs.existsSync(configDir)) {
16+
fs.ensureFileSync(configPath)
17+
fs.writeFileSync(configPath, JSON.stringify({}))
18+
}
19+
20+
return configPath
1421
}
1522

1623
export function setAccessTokenConfig(token: string) {
@@ -26,9 +33,17 @@ export function setAccessTokenConfig(token: string) {
2633

2734
export function safeStdinRead() {
2835
let result
29-
const STDIN_FD = 0
3036
try {
31-
result = fs.readFileSync(STDIN_FD).toString()
37+
result = fs.readFileSync(process.stdin.fd).toString()
3238
} catch {}
3339
return result
3440
}
41+
42+
// generate temporary markdown file in /tmp directory
43+
export function temporaryMD() {
44+
const tmpDir = tmpdir()
45+
const filename = `temp_${Math.random().toString(36).substring(2)}.md`
46+
const filePath = path.join(tmpDir, filename)
47+
48+
return filePath
49+
}

0 commit comments

Comments
 (0)