Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wiki-js
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
wiki-js
Commits
9e875794
Unverified
Commit
9e875794
authored
Apr 23, 2023
by
NGPixel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: markdown editor with monaco (wip)
parent
97bcc56c
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
829 additions
and
107 deletions
+829
-107
package-lock.json
ux/package-lock.json
+0
-0
package.json
ux/package.json
+1
-0
quasar.config.js
ux/quasar.config.js
+14
-1
monaco.js
ux/src/boot/monaco.js
+23
-0
EditorMarkdown.vue
ux/src/components/EditorMarkdown.vue
+170
-106
monacoTypes.js
ux/src/helpers/monacoTypes.js
+621
-0
No files found.
ux/package-lock.json
View file @
9e875794
B
{
...
...
ux/package.json
View file @
9e875794
...
...
@@ -73,6 +73,7 @@
"markdown-it-sup"
:
"1.0.0"
,
"markdown-it-task-lists"
:
"2.1.1"
,
"mitt"
:
"3.0.0"
,
"monaco-editor"
:
"0.37.1"
,
"pako"
:
"2.1.0"
,
"pinia"
:
"2.0.33"
,
"prosemirror-commands"
:
"1.5.1"
,
...
...
ux/quasar.config.js
View file @
9e875794
...
...
@@ -39,7 +39,11 @@ module.exports = configure(function (/* ctx */) {
'apollo'
,
'components'
,
'eventbus'
,
'i18n'
'i18n'
,
{
server
:
false
,
path
:
'monaco'
}
],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
...
...
@@ -99,6 +103,15 @@ module.exports = configure(function (/* ctx */) {
'prosemirror-model'
,
'prosemirror-view'
]
viteConf
.
build
.
rollupOptions
=
{
external
:
[
'monaco-editor'
],
output
:
{
globals
:
{
'monaco-editor'
:
'monaco-editor'
}
}
}
},
// viteVuePluginOptions: {},
...
...
ux/src/boot/monaco.js
0 → 100644
View file @
9e875794
import
EditorWorker
from
'monaco-editor/esm/vs/editor/editor.worker?worker'
import
JsonWorker
from
'monaco-editor/esm/vs/language/json/json.worker?worker'
import
CssWorker
from
'monaco-editor/esm/vs/language/css/css.worker?worker'
import
HtmlWorker
from
'monaco-editor/esm/vs/language/html/html.worker?worker'
import
TsWorker
from
'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
self
.
MonacoEnvironment
=
{
getWorker
(
_
,
label
)
{
if
(
label
===
'json'
)
{
return
new
JsonWorker
()
}
if
(
label
===
'css'
||
label
===
'scss'
||
label
===
'less'
)
{
return
new
CssWorker
()
}
if
(
label
===
'html'
||
label
===
'handlebars'
||
label
===
'razor'
)
{
return
new
HtmlWorker
()
}
if
(
label
===
'typescript'
||
label
===
'javascript'
)
{
return
new
TsWorker
()
}
return
new
EditorWorker
()
}
}
ux/src/components/EditorMarkdown.vue
View file @
9e875794
...
...
@@ -201,10 +201,10 @@
)
q
-
tooltip
(
anchor
=
'top middle'
self
=
'bottom middle'
)
{{
t
(
'editor.togglePreviewPane'
)
}}
//--------------------------------------------------------
//-
CODEMIRR
OR
//-
MONACO EDIT
OR
//--------------------------------------------------------
.
editor
-
markdown
-
editor
textarea
(
ref
=
'cm
Ref'
)
div
(
ref
=
'monaco
Ref'
)
transition
(
name
=
'editor-markdown-preview'
)
.
editor
-
markdown
-
preview
(
v
-
if
=
'state.previewShown'
)
.
editor
-
markdown
-
preview
-
toolbar
...
...
@@ -238,30 +238,14 @@ import { useMeta, useQuasar, setCssVar } from 'quasar'
import
{
useI18n
}
from
'vue-i18n'
import
{
get
,
flatten
,
last
,
times
,
startsWith
,
debounce
}
from
'lodash-es'
import
{
DateTime
}
from
'luxon'
import
*
as
monaco
from
'monaco-editor'
import
{
Position
,
Range
}
from
'monaco-editor'
import
{
WorkspaceEdit
}
from
'../helpers/monacoTypes'
import
{
useEditorStore
}
from
'src/stores/editor'
import
{
usePageStore
}
from
'src/stores/page'
import
{
useSiteStore
}
from
'src/stores/site'
// Code Mirror
import
CodeMirror
from
'codemirror'
import
'codemirror/lib/codemirror.css'
import
'../css/codemirror.scss'
// Language
import
'codemirror/mode/markdown/markdown.js'
// Addons
import
'codemirror/addon/selection/active-line.js'
import
'codemirror/addon/display/fullscreen.js'
import
'codemirror/addon/display/fullscreen.css'
import
'codemirror/addon/selection/mark-selection.js'
import
'codemirror/addon/search/searchcursor.js'
import
'codemirror/addon/hint/show-hint.js'
import
'codemirror/addon/fold/foldcode.js'
import
'codemirror/addon/fold/foldgutter.js'
import
'codemirror/addon/fold/foldgutter.css'
// Markdown Renderer
import
{
MarkdownRenderer
}
from
'src/renderers/markdown'
...
...
@@ -281,8 +265,9 @@ const { t } = useI18n()
// STATE
let
editor
const
cm
=
shallowRef
(
null
)
const
cm
Ref
=
ref
(
null
)
const
monaco
Ref
=
ref
(
null
)
const
state
=
reactive
({
previewShown
:
true
,
...
...
@@ -326,8 +311,8 @@ function insertTable () {
}
/**
* Set current line as header
*/
* Set current line as header
*/
function
setHeaderLine
(
lvl
)
{
const
curLine
=
cm
.
value
.
doc
.
getCursor
(
'head'
).
line
let
lineContent
=
cm
.
value
.
doc
.
getLine
(
curLine
)
...
...
@@ -340,8 +325,8 @@ function setHeaderLine (lvl) {
}
/**
* Get the header lever of the current line
*/
* Get the header lever of the current line
*/
function
getHeaderLevel
(
cm
)
{
const
curLine
=
cm
.
doc
.
getCursor
(
'head'
).
line
const
lineContent
=
cm
.
doc
.
getLine
(
curLine
)
...
...
@@ -354,16 +339,16 @@ function getHeaderLevel (cm) {
}
/**
* Insert content at cursor
*/
* Insert content at cursor
*/
function
insertAtCursor
({
content
}
)
{
const
cursor
=
cm
.
value
.
doc
.
getCursor
(
'head'
)
cm
.
value
.
doc
.
replaceRange
(
content
,
cursor
)
}
/**
* Insert content after current line
*/
* Insert content after current line
*/
function
insertAfter
({
content
,
newLine
}
)
{
const
curLine
=
cm
.
value
.
doc
.
getCursor
(
'to'
).
line
const
lineLength
=
cm
.
value
.
doc
.
getLine
(
curLine
).
length
...
...
@@ -371,8 +356,8 @@ function insertAfter ({ content, newLine }) {
}
/**
* Insert content before current line
*/
* Insert content before current line
*/
function
insertBeforeEachLine
({
content
,
after
}
)
{
let
lines
=
[]
if
(
!
cm
.
value
.
doc
.
somethingSelected
())
{
...
...
@@ -399,27 +384,40 @@ function insertBeforeEachLine ({ content, after }) {
}
/**
* Insert an Horizontal Bar
*/
* Insert an Horizontal Bar
*/
function
insertHorizontalBar
()
{
insertAfter
({
content
:
'---'
,
newLine
:
true
}
)
}
/**
* Toggle Markup at selection
*/
function
toggleMarkup
({
start
,
end
}
)
{
* Toggle Markup at selection
*/
async
function
toggleMarkup
({
start
,
end
}
)
{
if
(
!
end
)
{
end
=
start
}
if
(
!
cm
.
value
.
doc
.
somethingSelected
())
{
if
(
!
editor
.
getSelection
())
{
return
$q
.
notify
({
type
:
'negative'
,
message
:
t
(
'editor.markup.noSelectionError'
)
}
)
}
cm
.
value
.
doc
.
replaceSelections
(
cm
.
value
.
doc
.
getSelections
().
map
(
s
=>
start
+
s
+
end
))
}
const
onCmInput
=
debounce
(
processContent
,
500
)
const
edits
=
[]
for
(
const
selection
of
editor
.
getSelections
())
{
const
selectedText
=
editor
.
getModel
().
getValueInRange
(
selection
)
if
(
!
selectedText
)
{
const
word
=
editor
.
getModel
().
getWordAtPosition
(
selection
.
getPosition
())
}
if
(
selectedText
.
startsWith
(
start
)
&&
selectedText
.
endsWith
(
end
))
{
edits
.
push
({
range
:
selection
,
text
:
selectedText
.
substring
(
start
.
length
,
selectedText
.
length
-
end
.
length
)
}
)
}
else
{
edits
.
push
({
range
:
selection
,
text
:
`${start
}
${selectedText
}
${end
}
`
}
)
}
}
editor
.
executeEdits
(
''
,
edits
)
}
function
processContent
(
newContent
)
{
pageStore
.
$patch
({
...
...
@@ -435,75 +433,137 @@ onMounted(async () => {
hideSideNav
:
true
}
)
// -> Initialize CodeMirror
cm
.
value
=
CodeMirror
.
fromTextArea
(
cmRef
.
value
,
{
tabSize
:
2
,
mode
:
'text/markdown'
,
theme
:
'wikijs-dark'
,
lineNumbers
:
true
,
lineWrapping
:
true
,
line
:
true
,
styleActiveLine
:
true
,
highlightSelectionMatches
:
{
annotateScrollbar
:
true
}
,
viewportMargin
:
50
,
inputStyle
:
'contenteditable'
,
allowDropFileTypes
:
[
'image/jpg'
,
'image/png'
,
'image/svg'
,
'image/jpeg'
,
'image/gif'
],
// direction: siteConfig.rtl ? 'rtl' : 'ltr',
foldGutter
:
true
,
gutters
:
[
'CodeMirror-linenumbers'
,
'CodeMirror-foldgutter'
]
// -> Define Monaco Theme
monaco
.
editor
.
defineTheme
(
'wikijs'
,
{
base
:
'vs-dark'
,
inherit
:
true
,
rules
:
[],
colors
:
{
'editor.background'
:
'#070a0d'
,
'editor.lineHighlightBackground'
:
'#0d1117'
,
'editorLineNumber.foreground'
:
'#546e7a'
,
'editorGutter.background'
:
'#1e232a'
}
}
)
// -> Initialize Monaco Editor
editor
=
monaco
.
editor
.
create
(
monacoRef
.
value
,
{
value
:
pageStore
.
content
,
language
:
'markdown'
,
theme
:
'wikijs'
,
automaticLayout
:
true
,
scrollBeyondLastLine
:
false
,
fontSize
:
16
,
formatOnType
:
true
,
lineNumbersMinChars
:
3
}
)
window
.
edd
=
editor
// -> Define Formatting Actions
editor
.
addAction
({
contextMenuGroupId
:
'markdown.extension.editing'
,
contextMenuOrder
:
0
,
id
:
'markdown.extension.editing.toggleBold'
,
keybindings
:
[
monaco
.
KeyMod
.
CtrlCmd
|
monaco
.
KeyCode
.
KeyB
],
label
:
'Toggle bold'
,
precondition
:
''
,
run
(
ed
)
{
toggleMarkup
({
start
:
'**'
}
)
}
}
)
cm
.
value
.
setValue
(
pageStore
.
content
)
cm
.
value
.
on
(
'change'
,
c
=>
{
editor
.
addAction
({
contextMenuGroupId
:
'markdown.extension.editing'
,
contextMenuOrder
:
0
,
id
:
'markdown.extension.editing.toggleItalic'
,
keybindings
:
[
monaco
.
KeyMod
.
CtrlCmd
|
monaco
.
KeyCode
.
KeyI
],
label
:
'Toggle italic'
,
precondition
:
''
,
run
(
ed
)
{
toggleMarkup
({
start
:
'*'
}
)
}
}
)
editor
.
onDidChangeModelContent
(
debounce
(
ev
=>
{
editorStore
.
$patch
({
lastChangeTimestamp
:
DateTime
.
utc
()
}
)
pageStore
.
$patch
({
content
:
c
.
getValue
()
content
:
editor
.
getValue
()
}
)
onCmInput
(
pageStore
.
content
)
}
)
processContent
(
pageStore
.
content
)
}
,
500
))
// -> Initialize CodeMirror
// cm.value = CodeMirror.fromTextArea(cmRef.value,
{
// tabSize: 2,
// mode: 'text/markdown',
// theme: 'wikijs-dark',
// lineNumbers: true,
// lineWrapping: true,
// line: true,
// styleActiveLine: true,
// highlightSelectionMatches:
{
// annotateScrollbar: true
//
}
,
// viewportMargin: 50,
// inputStyle: 'contenteditable',
// allowDropFileTypes: ['image/jpg', 'image/png', 'image/svg', 'image/jpeg', 'image/gif'],
// // direction: siteConfig.rtl ? 'rtl' : 'ltr',
// foldGutter: true,
// gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
//
}
)
cm
.
value
.
setSize
(
null
,
'100%'
)
// cm.value.setValue(pageStore.content)
// cm.value.on('change', c =>
{
// editorStore.$patch(
{
// lastChangeTimestamp: DateTime.utc()
//
}
)
// pageStore.$patch(
{
// content: c.getValue()
//
}
)
// onCmInput(pageStore.content)
//
}
)
// cm.value.setSize(null, '100%')
// -> Set Keybindings
const
keyBindings
=
{
'F11'
(
c
)
{
c
.
setOption
(
'fullScreen'
,
!
c
.
getOption
(
'fullScreen'
))
}
,
'Esc'
(
c
)
{
if
(
c
.
getOption
(
'fullScreen'
))
{
c
.
setOption
(
'fullScreen'
,
false
)
}
}
,
[
`${CtrlKey
}
-S`
]
(
c
)
{
// save()
return
false
}
,
[
`${CtrlKey
}
-B`
]
(
c
)
{
toggleMarkup
({
start
:
'**'
}
)
return
false
}
,
[
`${CtrlKey
}
-I`
]
(
c
)
{
toggleMarkup
({
start
:
'*'
}
)
return
false
}
,
[
`${CtrlKey
}
-Alt-Right`
]
(
c
)
{
let
lvl
=
getHeaderLevel
(
c
)
if
(
lvl
>=
6
)
{
lvl
=
5
}
setHeaderLine
(
lvl
+
1
)
return
false
}
,
[
`${CtrlKey
}
-Alt-Left`
]
(
c
)
{
let
lvl
=
getHeaderLevel
(
c
)
if
(
lvl
<=
1
)
{
lvl
=
2
}
setHeaderLine
(
lvl
-
1
)
return
false
}
}
cm
.
value
.
setOption
(
'extraKeys'
,
keyBindings
)
//
const keyBindings =
{
//
'F11' (c)
{
//
c.setOption('fullScreen', !c.getOption('fullScreen'))
//
}
,
//
'Esc' (c)
{
//
if (c.getOption('fullScreen'))
{
//
c.setOption('fullScreen', false)
//
}
//
}
,
//
[`$
{
CtrlKey
}
-
S
`] (c) {
//
// save()
//
return false
//
}
,
//
[`
$
{
CtrlKey
}
-
B
`] (c) {
//
toggleMarkup({ start: '**'
}
)
//
return false
//
}
,
//
[`
$
{
CtrlKey
}
-
I
`] (c) {
//
toggleMarkup({ start: '*'
}
)
//
return false
//
}
,
//
[`
$
{
CtrlKey
}
-
Alt
-
Right
`] (c) {
//
let lvl = getHeaderLevel(c)
//
if (lvl >= 6) { lvl = 5
}
//
setHeaderLine(lvl + 1)
//
return false
//
}
,
//
[`
$
{
CtrlKey
}
-
Alt
-
Left
`] (c) {
//
let lvl = getHeaderLevel(c)
//
if (lvl <= 1) { lvl = 2
}
//
setHeaderLine(lvl - 1)
//
return false
//
}
//
}
//
cm.value.setOption('extraKeys', keyBindings)
// this.cm.on('inputRead', this.autocomplete)
// // Handle cursor movement
...
...
@@ -516,11 +576,11 @@ onMounted(async () => {
// this.cm.on('paste', this.onCmPaste)
// // Render initial preview
processContent
(
pageStore
.
content
)
nextTick
(()
=>
{
cm
.
value
.
refresh
()
cm
.
value
.
focus
()
}
)
//
processContent(pageStore.content)
//
nextTick(() => {
//
cm.value.refresh()
//
cm.value.focus()
//
}
)
EVENT_BUS.on('insertAsset', insertAssetClb)
...
...
@@ -589,6 +649,10 @@ $editor-height-mobile: calc(100vh - 112px - 16px);
// @include until($tablet) {
// height: $editor-height-mobile;
//
}
> div {
height: 100%;
}
}
&-type {
writing-mode: vertical-rl;
...
...
@@ -693,7 +757,7 @@ $editor-height-mobile: calc(100vh - 112px - 16px);
}
&-toolbar {
background-color: $primary;
border-left:
4
0px solid darken($primary, 5%);
border-left:
5
0px solid darken($primary, 5%);
color: #FFF;
height: 32px;
}
...
...
ux/src/helpers/monacoTypes.js
0 → 100644
View file @
9e875794
// Adapted from https://github.com/trofimander/monaco-markdown/blob/master/src/ts/extHostTypes.ts
// by https://github.com/trofimander
// MIT Licensed
// export function values<V = any>(set: Set<V>): V[];
// export function values<K = any, V = any>(map: Map<K, V>): V[];
export
function
values
(
forEachable
)
{
const
result
=
[]
forEachable
.
forEach
(
value
=>
result
.
push
(
value
))
return
result
}
export
class
Position
{
static
Min
(...
positions
)
{
if
(
positions
.
length
===
0
)
{
throw
new
TypeError
()
}
let
result
=
positions
[
0
]
for
(
let
i
=
1
;
i
<
positions
.
length
;
i
++
)
{
const
p
=
positions
[
i
]
if
(
p
.
isBefore
(
result
))
{
result
=
p
}
}
return
result
}
static
Max
(...
positions
)
{
if
(
positions
.
length
===
0
)
{
throw
new
TypeError
()
}
let
result
=
positions
[
0
]
for
(
let
i
=
1
;
i
<
positions
.
length
;
i
++
)
{
const
p
=
positions
[
i
]
if
(
p
.
isAfter
(
result
))
{
result
=
p
}
}
return
result
}
static
isPosition
(
other
)
{
if
(
!
other
)
{
return
false
}
if
(
other
instanceof
Position
)
{
return
true
}
const
{
line
,
character
}
=
other
if
(
typeof
line
===
'number'
&&
typeof
character
===
'number'
)
{
return
true
}
return
false
}
get
line
()
{
return
this
.
_line
}
get
character
()
{
return
this
.
_character
}
constructor
(
line
,
character
)
{
if
(
line
<
0
)
{
throw
new
Error
(
'line must be non-negative'
)
}
if
(
character
<
0
)
{
throw
new
Error
(
'character must be non-negative'
)
}
this
.
_line
=
line
this
.
_character
=
character
}
isBefore
(
other
)
{
if
(
this
.
_line
<
other
.
_line
)
{
return
true
}
if
(
other
.
_line
<
this
.
_line
)
{
return
false
}
return
this
.
_character
<
other
.
_character
}
isBeforeOrEqual
(
other
)
{
if
(
this
.
_line
<
other
.
_line
)
{
return
true
}
if
(
other
.
_line
<
this
.
_line
)
{
return
false
}
return
this
.
_character
<=
other
.
_character
}
isAfter
(
other
)
{
return
!
this
.
isBeforeOrEqual
(
other
)
}
isAfterOrEqual
(
other
)
{
return
!
this
.
isBefore
(
other
)
}
isEqual
(
other
)
{
return
this
.
_line
===
other
.
_line
&&
this
.
_character
===
other
.
_character
}
compareTo
(
other
)
{
if
(
this
.
_line
<
other
.
_line
)
{
return
-
1
}
else
if
(
this
.
_line
>
other
.
line
)
{
return
1
}
else
{
// equal line
if
(
this
.
_character
<
other
.
_character
)
{
return
-
1
}
else
if
(
this
.
_character
>
other
.
_character
)
{
return
1
}
else
{
// equal line and character
return
0
}
}
}
translate
(
lineDeltaOrChange
,
characterDelta
=
0
)
{
if
(
lineDeltaOrChange
===
null
||
characterDelta
===
null
)
{
throw
new
Error
()
}
let
lineDelta
if
(
typeof
lineDeltaOrChange
===
'undefined'
)
{
lineDelta
=
0
}
else
if
(
typeof
lineDeltaOrChange
===
'number'
)
{
lineDelta
=
lineDeltaOrChange
}
else
{
lineDelta
=
typeof
lineDeltaOrChange
.
lineDelta
===
'number'
?
lineDeltaOrChange
.
lineDelta
:
0
characterDelta
=
typeof
lineDeltaOrChange
.
characterDelta
===
'number'
?
lineDeltaOrChange
.
characterDelta
:
0
}
if
(
lineDelta
===
0
&&
characterDelta
===
0
)
{
return
this
}
return
new
Position
(
this
.
line
+
lineDelta
,
this
.
character
+
characterDelta
)
}
with
(
lineOrChange
,
character
=
this
.
character
)
{
if
(
lineOrChange
===
null
||
character
===
null
)
{
throw
new
Error
()
}
let
line
if
(
typeof
lineOrChange
===
'undefined'
)
{
line
=
this
.
line
}
else
if
(
typeof
lineOrChange
===
'number'
)
{
line
=
lineOrChange
}
else
{
line
=
typeof
lineOrChange
.
line
===
'number'
?
lineOrChange
.
line
:
this
.
line
character
=
typeof
lineOrChange
.
character
===
'number'
?
lineOrChange
.
character
:
this
.
character
}
if
(
line
===
this
.
line
&&
character
===
this
.
character
)
{
return
this
}
return
new
Position
(
line
,
character
)
}
toJSON
()
{
return
{
line
:
this
.
line
,
character
:
this
.
character
}
}
}
export
class
Range
{
static
isRange
(
thing
)
{
if
(
thing
instanceof
Range
)
{
return
true
}
if
(
!
thing
)
{
return
false
}
return
Position
.
isPosition
(
thing
.
start
)
&&
Position
.
isPosition
(
thing
.
end
)
}
get
start
()
{
return
this
.
_start
}
get
end
()
{
return
this
.
_end
}
constructor
(
startLineOrStart
,
startColumnOrEnd
,
endLine
,
endColumn
)
{
let
start
let
end
if
(
typeof
startLineOrStart
===
'number'
&&
typeof
startColumnOrEnd
===
'number'
&&
typeof
endLine
===
'number'
&&
typeof
endColumn
===
'number'
)
{
start
=
new
Position
(
startLineOrStart
,
startColumnOrEnd
)
end
=
new
Position
(
endLine
,
endColumn
)
}
else
if
(
startLineOrStart
instanceof
Position
&&
startColumnOrEnd
instanceof
Position
)
{
start
=
startLineOrStart
end
=
startColumnOrEnd
}
if
(
!
start
||
!
end
)
{
throw
new
Error
(
'Invalid arguments'
)
}
if
(
start
.
isBefore
(
end
))
{
this
.
_start
=
start
this
.
_end
=
end
}
else
{
this
.
_start
=
end
this
.
_end
=
start
}
}
contains
(
positionOrRange
)
{
if
(
positionOrRange
instanceof
Range
)
{
return
this
.
contains
(
positionOrRange
.
_start
)
&&
this
.
contains
(
positionOrRange
.
_end
)
}
else
if
(
positionOrRange
instanceof
Position
)
{
if
(
positionOrRange
.
isBefore
(
this
.
_start
))
{
return
false
}
if
(
this
.
_end
.
isBefore
(
positionOrRange
))
{
return
false
}
return
true
}
return
false
}
isEqual
(
other
)
{
return
this
.
_start
.
isEqual
(
other
.
_start
)
&&
this
.
_end
.
isEqual
(
other
.
_end
)
}
intersection
(
other
)
{
const
start
=
Position
.
Max
(
other
.
start
,
this
.
_start
)
const
end
=
Position
.
Min
(
other
.
end
,
this
.
_end
)
if
(
start
.
isAfter
(
end
))
{
// this happens when there is no overlap:
// |-----|
// |----|
return
undefined
}
return
new
Range
(
start
,
end
)
}
union
(
other
)
{
if
(
this
.
contains
(
other
))
{
return
this
}
else
if
(
other
.
contains
(
this
))
{
return
other
}
const
start
=
Position
.
Min
(
other
.
start
,
this
.
_start
)
const
end
=
Position
.
Max
(
other
.
end
,
this
.
end
)
return
new
Range
(
start
,
end
)
}
get
isEmpty
()
{
return
this
.
_start
.
isEqual
(
this
.
_end
)
}
get
isSingleLine
()
{
return
this
.
_start
.
line
===
this
.
_end
.
line
}
with
(
startOrChange
,
end
=
this
.
end
)
{
if
(
startOrChange
===
null
||
end
===
null
)
{
throw
new
Error
()
}
let
start
if
(
!
startOrChange
)
{
start
=
this
.
start
}
else
if
(
Position
.
isPosition
(
startOrChange
))
{
start
=
startOrChange
}
else
{
start
=
startOrChange
.
start
||
this
.
start
end
=
startOrChange
.
end
||
this
.
end
}
if
(
start
.
isEqual
(
this
.
_start
)
&&
end
.
isEqual
(
this
.
end
))
{
return
this
}
return
new
Range
(
start
,
end
)
}
toJSON
()
{
return
[
this
.
start
,
this
.
end
]
}
}
export
class
Selection
extends
Range
{
static
isSelection
(
thing
)
{
if
(
thing
instanceof
Selection
)
{
return
true
}
if
(
!
thing
)
{
return
false
}
return
Range
.
isRange
(
thing
)
&&
Position
.
isPosition
(
thing
.
anchor
)
&&
Position
.
isPosition
(
thing
.
active
)
&&
typeof
thing
.
isReversed
===
'boolean'
}
get
anchor
()
{
return
this
.
_anchor
}
get
active
()
{
return
this
.
_active
}
constructor
(
anchorLineOrAnchor
,
anchorColumnOrActive
,
activeLine
,
activeColumn
)
{
let
anchor
let
active
if
(
typeof
anchorLineOrAnchor
===
'number'
&&
typeof
anchorColumnOrActive
===
'number'
&&
typeof
activeLine
===
'number'
&&
typeof
activeColumn
===
'number'
)
{
anchor
=
new
Position
(
anchorLineOrAnchor
,
anchorColumnOrActive
)
active
=
new
Position
(
activeLine
,
activeColumn
)
}
else
if
(
anchorLineOrAnchor
instanceof
Position
&&
anchorColumnOrActive
instanceof
Position
)
{
anchor
=
anchorLineOrAnchor
active
=
anchorColumnOrActive
}
if
(
!
anchor
||
!
active
)
{
throw
new
Error
(
'Invalid arguments'
)
}
super
(
anchor
,
active
)
this
.
_anchor
=
anchor
this
.
_active
=
active
}
get
isReversed
()
{
return
this
.
_anchor
===
this
.
_end
}
toJSON
()
{
return
{
start
:
this
.
start
,
end
:
this
.
end
,
active
:
this
.
active
,
anchor
:
this
.
anchor
}
}
}
export
const
EndOfLine
=
{
LF
:
1
,
CRLF
:
2
}
export
class
TextEdit
{
static
isTextEdit
(
thing
)
{
if
(
thing
instanceof
TextEdit
)
{
return
true
}
if
(
!
thing
)
{
return
false
}
return
Range
.
isRange
(
thing
)
&&
typeof
thing
.
newText
===
'string'
}
static
replace
(
range
,
newText
)
{
return
new
TextEdit
(
range
,
newText
)
}
static
insert
(
position
,
newText
)
{
return
TextEdit
.
replace
(
new
Range
(
position
,
position
),
newText
)
}
static
delete
(
range
)
{
return
TextEdit
.
replace
(
range
,
''
)
}
static
setEndOfLine
(
eol
)
{
const
ret
=
new
TextEdit
(
new
Range
(
new
Position
(
0
,
0
),
new
Position
(
0
,
0
)),
''
)
ret
.
newEol
=
eol
return
ret
}
get
range
()
{
return
this
.
_range
}
set
range
(
value
)
{
if
(
value
&&
!
Range
.
isRange
(
value
))
{
throw
new
Error
(
'range'
)
}
this
.
_range
=
value
}
get
newText
()
{
return
this
.
_newText
||
''
}
set
newText
(
value
)
{
if
(
value
&&
typeof
value
!==
'string'
)
{
throw
new
Error
(
'newText'
)
}
this
.
_newText
=
value
}
get
newEol
()
{
return
this
.
_newEol
}
set
newEol
(
value
)
{
if
(
value
&&
typeof
value
!==
'number'
)
{
throw
new
Error
(
'newEol'
)
}
this
.
_newEol
=
value
}
constructor
(
range
,
newText
)
{
this
.
range
=
range
this
.
_newText
=
newText
}
toJSON
()
{
return
{
range
:
this
.
range
,
newText
:
this
.
newText
,
newEol
:
this
.
_newEol
}
}
}
export
class
WorkspaceEdit
{
constructor
()
{
this
.
_edits
=
[]
}
renameFile
(
from
,
to
,
options
)
{
this
.
_edits
.
push
({
_type
:
1
,
from
,
to
,
options
})
}
createFile
(
uri
,
options
)
{
this
.
_edits
.
push
({
_type
:
1
,
from
:
undefined
,
to
:
uri
,
options
})
}
deleteFile
(
uri
,
options
)
{
this
.
_edits
.
push
({
_type
:
1
,
from
:
uri
,
to
:
undefined
,
options
})
}
replace
(
uri
,
range
,
newText
)
{
this
.
_edits
.
push
({
_type
:
2
,
uri
,
edit
:
new
TextEdit
(
range
,
newText
)
})
}
insert
(
resource
,
position
,
newText
)
{
this
.
replace
(
resource
,
new
Range
(
position
,
position
),
newText
)
}
delete
(
resource
,
range
)
{
this
.
replace
(
resource
,
range
,
''
)
}
has
(
uri
)
{
for
(
const
edit
of
this
.
_edits
)
{
if
(
edit
.
_type
===
2
&&
edit
.
uri
.
toString
()
===
uri
.
toString
())
{
return
true
}
}
return
false
}
set
(
uri
,
edits
)
{
if
(
!
edits
)
{
// remove all text edits for `uri`
for
(
let
i
=
0
;
i
<
this
.
_edits
.
length
;
i
++
)
{
const
element
=
this
.
_edits
[
i
]
if
(
element
.
_type
===
2
&&
element
.
uri
.
toString
()
===
uri
.
toString
())
{
this
.
_edits
[
i
]
=
undefined
// will be coalesced down below
}
}
// this._edits = coalesce(this._edits); TODO
}
else
{
// append edit to the end
for
(
const
edit
of
edits
)
{
if
(
edit
)
{
this
.
_edits
.
push
({
_type
:
2
,
uri
,
edit
})
}
}
}
}
get
(
uri
)
{
const
res
=
[]
for
(
const
candidate
of
this
.
_edits
)
{
if
(
candidate
.
_type
===
2
&&
candidate
.
uri
.
toString
()
===
uri
.
toString
())
{
res
.
push
(
candidate
.
edit
)
}
}
return
res
}
entries
()
{
const
textEdits
=
new
Map
()
for
(
const
candidate
of
this
.
_edits
)
{
if
(
candidate
.
_type
===
2
)
{
let
textEdit
=
textEdits
.
get
(
candidate
.
uri
.
toString
())
if
(
!
textEdit
)
{
textEdit
=
[
candidate
.
uri
,
[]]
textEdits
.
set
(
candidate
.
uri
.
toString
(),
textEdit
)
}
textEdit
[
1
].
push
(
candidate
.
edit
)
}
}
return
values
(
textEdits
)
}
_allEntries
()
{
const
res
=
[]
for
(
const
edit
of
this
.
_edits
)
{
if
(
edit
.
_type
===
1
)
{
res
.
push
([
edit
.
from
,
edit
.
to
,
edit
.
options
])
}
else
{
res
.
push
([
edit
.
uri
,
[
edit
.
edit
]])
}
}
return
res
}
get
size
()
{
return
this
.
entries
().
length
}
toJSON
()
{
return
this
.
entries
()
}
}
export
const
TextEditorRevealType
=
{
Default
:
0
,
InCenter
:
1
,
InCenterIfOutsideViewport
:
2
,
AtTop
:
3
}
export
const
TextEditorSelectionChangeKind
=
{
Keyboard
:
1
,
Mouse
:
2
,
Command
:
3
}
export
class
SnippetString
{
static
isSnippetString
(
thing
)
{
if
(
thing
instanceof
SnippetString
)
{
return
true
}
if
(
!
thing
)
{
return
false
}
return
typeof
thing
.
value
===
'string'
}
static
_escape
(
value
)
{
return
value
.
replace
(
/
\$
|}|
\\
/g
,
'
\\
$&'
)
}
constructor
(
value
)
{
this
.
_tabstop
=
1
this
.
value
=
value
||
''
}
appendText
(
string
)
{
this
.
value
+=
SnippetString
.
_escape
(
string
)
return
this
}
appendTabstop
(
number
=
this
.
_tabstop
++
)
{
this
.
value
+=
'$'
this
.
value
+=
number
return
this
}
appendPlaceholder
(
value
,
number
=
this
.
_tabstop
++
)
{
if
(
typeof
value
===
'function'
)
{
const
nested
=
new
SnippetString
()
nested
.
_tabstop
=
this
.
_tabstop
value
(
nested
)
this
.
_tabstop
=
nested
.
_tabstop
value
=
nested
.
value
}
else
{
value
=
SnippetString
.
_escape
(
value
)
}
this
.
value
+=
'${'
this
.
value
+=
number
this
.
value
+=
':'
this
.
value
+=
value
this
.
value
+=
'}'
return
this
}
appendVariable
(
name
,
defaultValue
)
{
if
(
typeof
defaultValue
===
'function'
)
{
const
nested
=
new
SnippetString
()
nested
.
_tabstop
=
this
.
_tabstop
defaultValue
(
nested
)
this
.
_tabstop
=
nested
.
_tabstop
defaultValue
=
nested
.
value
}
else
if
(
typeof
defaultValue
===
'string'
)
{
defaultValue
=
defaultValue
.
replace
(
/
\$
|}/g
,
'
\\
$&'
)
}
this
.
value
+=
'${'
this
.
value
+=
name
if
(
defaultValue
)
{
this
.
value
+=
':'
this
.
value
+=
defaultValue
}
this
.
value
+=
'}'
return
this
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment