forked from sourcegraph/sourcegraph-public-snapshot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSnippetsPage.tsx
145 lines (136 loc) · 6.49 KB
/
SnippetsPage.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import H from 'history'
import { uniqueId } from 'lodash'
import React, { createRef, useEffect, useLayoutEffect, useState } from 'react'
import { map } from 'rxjs/operators'
import { EditorId, observeEditorAndModel } from '../../../shared/src/api/client/services/editorService'
import { TextModel } from '../../../shared/src/api/client/services/modelService'
import { PanelViewWithComponent } from '../../../shared/src/api/client/services/panelViews'
import { SNIPPET_URI_SCHEME } from '../../../shared/src/api/client/types/textDocument'
import { ContributableViewContainer } from '../../../shared/src/api/protocol'
import { EditorTextField } from '../../../shared/src/components/editorTextField/EditorTextField'
import { WithLinkPreviews } from '../../../shared/src/components/linkPreviews/WithLinkPreviews'
import { Markdown } from '../../../shared/src/components/Markdown'
import { ExtensionsControllerProps } from '../../../shared/src/extensions/controller'
import { createLinkClickHandler } from '../../../shared/src/util/linkClickHandler'
import { renderMarkdown } from '../../../shared/src/util/markdown'
import { LINK_PREVIEW_CLASS } from '../components/linkPreviews/styles'
import { WebEditorCompletionWidget } from '../components/shared'
import { setElementTooltip } from '../components/tooltip/Tooltip'
interface Props extends ExtensionsControllerProps {
location: H.Location
history: H.History
}
/**
* Shows a text field for a snippet. This functionality is currently incomplete and is intended only
* to allow experimentation with extensions that listen for changes in documents and display
* Markdown-formatted text.
*/
export const SnippetsPage: React.FunctionComponent<Props> = ({ location, extensionsController, ...props }) => {
const [textArea, setTextArea] = useState<HTMLTextAreaElement | null>(null)
const textAreaRef = createRef<HTMLTextAreaElement>()
useLayoutEffect(() => setTextArea(textAreaRef.current), [textAreaRef])
const [editorId, setEditorId] = useState<EditorId | null>(null)
const [modelUri, setModelUri] = useState<string | null>(null)
const urlQuery = new URLSearchParams(location.search)
const textAreaClassName = urlQuery.has('mono') ? 'text-monospace' : ''
const initialModelUriScheme = urlQuery.get('scheme') || SNIPPET_URI_SCHEME
const initialModelLanguageId = urlQuery.get('languageId') || 'plaintext'
const initialModelText = urlQuery.get('text') || ''
useEffect(() => {
const model: TextModel = {
uri: uniqueId(`${initialModelUriScheme}://`),
languageId: initialModelLanguageId,
text: initialModelText,
}
extensionsController.services.model.addModel(model)
setModelUri(model.uri)
const editor = extensionsController.services.editor.addEditor({
type: 'CodeEditor',
resource: model.uri,
selections: [],
isActive: true,
})
setEditorId(editor)
return () => {
extensionsController.services.editor.removeEditor(editor)
}
}, [
initialModelUriScheme,
initialModelLanguageId,
initialModelText,
extensionsController.services.model,
extensionsController.services.editor,
])
const [panelViews, setPanelViews] = useState<PanelViewWithComponent[] | null>(null)
useEffect(() => {
const subscription = extensionsController.services.panelViews
.getPanelViews(ContributableViewContainer.Panel)
.subscribe(views => setPanelViews(views))
return () => subscription.unsubscribe()
}, [extensionsController.services.panelViews])
// Add Markdown panel for Markdown snippets.
const [modelText, setModelText] = useState<string | null>(null)
useEffect(() => {
if (!editorId) {
return () => undefined
}
const subscription = observeEditorAndModel(
editorId,
extensionsController.services.editor,
extensionsController.services.model
)
.pipe(map(editor => editor.model.text))
.subscribe(text => setModelText(text || null))
return () => subscription.unsubscribe()
}, [editorId, initialModelLanguageId, extensionsController.services.editor, extensionsController.services.model])
const allPanelViews: PanelViewWithComponent[] | null =
initialModelLanguageId === 'markdown' && modelText !== null
? [...(panelViews || []), { title: 'Preview', content: modelText, priority: 0 }]
: panelViews
return (
<div className="container mt-3">
<h1>
Snippet editor <span className="badge badge-warning">Experimental</span>
</h1>
{editorId && modelUri && (
<>
{textArea && (
<WebEditorCompletionWidget
textArea={textArea}
editorId={editorId.editorId}
extensionsController={extensionsController}
/>
)}
<EditorTextField
className={`form-control ${textAreaClassName || ''}`}
placeholder="Type a snippet"
editorId={editorId.editorId}
modelUri={modelUri}
autoFocus={true}
spellCheck={false}
rows={12}
textAreaRef={textAreaRef}
extensionsController={extensionsController}
/>
</>
)}
{allPanelViews &&
allPanelViews.length > 0 &&
allPanelViews.map((view, i) => (
<div key={i} className="mt-3 card">
<h3 className="card-header">{view.title}</h3>
<div className="card-body" onClick={createLinkClickHandler(props.history)}>
<WithLinkPreviews
dangerousInnerHTML={renderMarkdown(view.content)}
extensionsController={extensionsController}
setElementTooltip={setElementTooltip}
linkPreviewContentClass={LINK_PREVIEW_CLASS}
>
{props => <Markdown {...props} />}
</WithLinkPreviews>
</div>
</div>
))}
</div>
)
}