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
You need to sign in or sign up before continuing.
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
{
B
{
...
...
ux/package.json
View file @
9e875794
...
@@ -73,6 +73,7 @@
...
@@ -73,6 +73,7 @@
"markdown-it-sup"
:
"1.0.0"
,
"markdown-it-sup"
:
"1.0.0"
,
"markdown-it-task-lists"
:
"2.1.1"
,
"markdown-it-task-lists"
:
"2.1.1"
,
"mitt"
:
"3.0.0"
,
"mitt"
:
"3.0.0"
,
"monaco-editor"
:
"0.37.1"
,
"pako"
:
"2.1.0"
,
"pako"
:
"2.1.0"
,
"pinia"
:
"2.0.33"
,
"pinia"
:
"2.0.33"
,
"prosemirror-commands"
:
"1.5.1"
,
"prosemirror-commands"
:
"1.5.1"
,
...
...
ux/quasar.config.js
View file @
9e875794
...
@@ -39,7 +39,11 @@ module.exports = configure(function (/* ctx */) {
...
@@ -39,7 +39,11 @@ module.exports = configure(function (/* ctx */) {
'apollo'
,
'apollo'
,
'components'
,
'components'
,
'eventbus'
,
'eventbus'
,
'i18n'
'i18n'
,
{
server
:
false
,
path
:
'monaco'
}
],
],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
...
@@ -99,6 +103,15 @@ module.exports = configure(function (/* ctx */) {
...
@@ -99,6 +103,15 @@ module.exports = configure(function (/* ctx */) {
'prosemirror-model'
,
'prosemirror-model'
,
'prosemirror-view'
'prosemirror-view'
]
]
viteConf
.
build
.
rollupOptions
=
{
external
:
[
'monaco-editor'
],
output
:
{
globals
:
{
'monaco-editor'
:
'monaco-editor'
}
}
}
},
},
// viteVuePluginOptions: {},
// 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 @@
...
@@ -201,10 +201,10 @@
)
)
q
-
tooltip
(
anchor
=
'top middle'
self
=
'bottom middle'
)
{{
t
(
'editor.togglePreviewPane'
)
}}
q
-
tooltip
(
anchor
=
'top middle'
self
=
'bottom middle'
)
{{
t
(
'editor.togglePreviewPane'
)
}}
//--------------------------------------------------------
//--------------------------------------------------------
//-
CODEMIRR
OR
//-
MONACO EDIT
OR
//--------------------------------------------------------
//--------------------------------------------------------
.
editor
-
markdown
-
editor
.
editor
-
markdown
-
editor
textarea
(
ref
=
'cm
Ref'
)
div
(
ref
=
'monaco
Ref'
)
transition
(
name
=
'editor-markdown-preview'
)
transition
(
name
=
'editor-markdown-preview'
)
.
editor
-
markdown
-
preview
(
v
-
if
=
'state.previewShown'
)
.
editor
-
markdown
-
preview
(
v
-
if
=
'state.previewShown'
)
.
editor
-
markdown
-
preview
-
toolbar
.
editor
-
markdown
-
preview
-
toolbar
...
@@ -238,30 +238,14 @@ import { useMeta, useQuasar, setCssVar } from 'quasar'
...
@@ -238,30 +238,14 @@ import { useMeta, useQuasar, setCssVar } from 'quasar'
import
{
useI18n
}
from
'vue-i18n'
import
{
useI18n
}
from
'vue-i18n'
import
{
get
,
flatten
,
last
,
times
,
startsWith
,
debounce
}
from
'lodash-es'
import
{
get
,
flatten
,
last
,
times
,
startsWith
,
debounce
}
from
'lodash-es'
import
{
DateTime
}
from
'luxon'
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
{
useEditorStore
}
from
'src/stores/editor'
import
{
usePageStore
}
from
'src/stores/page'
import
{
usePageStore
}
from
'src/stores/page'
import
{
useSiteStore
}
from
'src/stores/site'
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
// Markdown Renderer
import
{
MarkdownRenderer
}
from
'src/renderers/markdown'
import
{
MarkdownRenderer
}
from
'src/renderers/markdown'
...
@@ -281,8 +265,9 @@ const { t } = useI18n()
...
@@ -281,8 +265,9 @@ const { t } = useI18n()
// STATE
// STATE
let
editor
const
cm
=
shallowRef
(
null
)
const
cm
=
shallowRef
(
null
)
const
cm
Ref
=
ref
(
null
)
const
monaco
Ref
=
ref
(
null
)
const
state
=
reactive
({
const
state
=
reactive
({
previewShown
:
true
,
previewShown
:
true
,
...
@@ -326,8 +311,8 @@ function insertTable () {
...
@@ -326,8 +311,8 @@ function insertTable () {
}
}
/**
/**
* Set current line as header
* Set current line as header
*/
*/
function
setHeaderLine
(
lvl
)
{
function
setHeaderLine
(
lvl
)
{
const
curLine
=
cm
.
value
.
doc
.
getCursor
(
'head'
).
line
const
curLine
=
cm
.
value
.
doc
.
getCursor
(
'head'
).
line
let
lineContent
=
cm
.
value
.
doc
.
getLine
(
curLine
)
let
lineContent
=
cm
.
value
.
doc
.
getLine
(
curLine
)
...
@@ -340,8 +325,8 @@ function setHeaderLine (lvl) {
...
@@ -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
)
{
function
getHeaderLevel
(
cm
)
{
const
curLine
=
cm
.
doc
.
getCursor
(
'head'
).
line
const
curLine
=
cm
.
doc
.
getCursor
(
'head'
).
line
const
lineContent
=
cm
.
doc
.
getLine
(
curLine
)
const
lineContent
=
cm
.
doc
.
getLine
(
curLine
)
...
@@ -354,16 +339,16 @@ function getHeaderLevel (cm) {
...
@@ -354,16 +339,16 @@ function getHeaderLevel (cm) {
}
}
/**
/**
* Insert content at cursor
* Insert content at cursor
*/
*/
function
insertAtCursor
({
content
}
)
{
function
insertAtCursor
({
content
}
)
{
const
cursor
=
cm
.
value
.
doc
.
getCursor
(
'head'
)
const
cursor
=
cm
.
value
.
doc
.
getCursor
(
'head'
)
cm
.
value
.
doc
.
replaceRange
(
content
,
cursor
)
cm
.
value
.
doc
.
replaceRange
(
content
,
cursor
)
}
}
/**
/**
* Insert content after current line
* Insert content after current line
*/
*/
function
insertAfter
({
content
,
newLine
}
)
{
function
insertAfter
({
content
,
newLine
}
)
{
const
curLine
=
cm
.
value
.
doc
.
getCursor
(
'to'
).
line
const
curLine
=
cm
.
value
.
doc
.
getCursor
(
'to'
).
line
const
lineLength
=
cm
.
value
.
doc
.
getLine
(
curLine
).
length
const
lineLength
=
cm
.
value
.
doc
.
getLine
(
curLine
).
length
...
@@ -371,8 +356,8 @@ function insertAfter ({ content, newLine }) {
...
@@ -371,8 +356,8 @@ function insertAfter ({ content, newLine }) {
}
}
/**
/**
* Insert content before current line
* Insert content before current line
*/
*/
function
insertBeforeEachLine
({
content
,
after
}
)
{
function
insertBeforeEachLine
({
content
,
after
}
)
{
let
lines
=
[]
let
lines
=
[]
if
(
!
cm
.
value
.
doc
.
somethingSelected
())
{
if
(
!
cm
.
value
.
doc
.
somethingSelected
())
{
...
@@ -399,27 +384,40 @@ function insertBeforeEachLine ({ content, after }) {
...
@@ -399,27 +384,40 @@ function insertBeforeEachLine ({ content, after }) {
}
}
/**
/**
* Insert an Horizontal Bar
* Insert an Horizontal Bar
*/
*/
function
insertHorizontalBar
()
{
function
insertHorizontalBar
()
{
insertAfter
({
content
:
'---'
,
newLine
:
true
}
)
insertAfter
({
content
:
'---'
,
newLine
:
true
}
)
}
}
/**
/**
* Toggle Markup at selection
* Toggle Markup at selection
*/
*/
function
toggleMarkup
({
start
,
end
}
)
{
async
function
toggleMarkup
({
start
,
end
}
)
{
if
(
!
end
)
{
end
=
start
}
if
(
!
end
)
{
end
=
start
}
if
(
!
cm
.
value
.
doc
.
somethingSelected
())
{
if
(
!
editor
.
getSelection
())
{
return
$q
.
notify
({
return
$q
.
notify
({
type
:
'negative'
,
type
:
'negative'
,
message
:
t
(
'editor.markup.noSelectionError'
)
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
)
{
function
processContent
(
newContent
)
{
pageStore
.
$patch
({
pageStore
.
$patch
({
...
@@ -435,75 +433,137 @@ onMounted(async () => {
...
@@ -435,75 +433,137 @@ onMounted(async () => {
hideSideNav
:
true
hideSideNav
:
true
}
)
}
)
// -> Initialize CodeMirror
// -> Define Monaco Theme
cm
.
value
=
CodeMirror
.
fromTextArea
(
cmRef
.
value
,
{
monaco
.
editor
.
defineTheme
(
'wikijs'
,
{
tabSize
:
2
,
base
:
'vs-dark'
,
mode
:
'text/markdown'
,
inherit
:
true
,
theme
:
'wikijs-dark'
,
rules
:
[],
lineNumbers
:
true
,
colors
:
{
lineWrapping
:
true
,
'editor.background'
:
'#070a0d'
,
line
:
true
,
'editor.lineHighlightBackground'
:
'#0d1117'
,
styleActiveLine
:
true
,
'editorLineNumber.foreground'
:
'#546e7a'
,
highlightSelectionMatches
:
{
'editorGutter.background'
:
'#1e232a'
annotateScrollbar
:
true
}
}
,
}
)
viewportMargin
:
50
,
inputStyle
:
'contenteditable'
,
// -> Initialize Monaco Editor
allowDropFileTypes
:
[
'image/jpg'
,
'image/png'
,
'image/svg'
,
'image/jpeg'
,
'image/gif'
],
editor
=
monaco
.
editor
.
create
(
monacoRef
.
value
,
{
// direction: siteConfig.rtl ? 'rtl' : 'ltr',
value
:
pageStore
.
content
,
foldGutter
:
true
,
language
:
'markdown'
,
gutters
:
[
'CodeMirror-linenumbers'
,
'CodeMirror-foldgutter'
]
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
)
editor
.
addAction
({
cm
.
value
.
on
(
'change'
,
c
=>
{
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
({
editorStore
.
$patch
({
lastChangeTimestamp
:
DateTime
.
utc
()
lastChangeTimestamp
:
DateTime
.
utc
()
}
)
}
)
pageStore
.
$patch
({
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
// -> Set Keybindings
const
keyBindings
=
{
//
const keyBindings =
{
'F11'
(
c
)
{
//
'F11' (c)
{
c
.
setOption
(
'fullScreen'
,
!
c
.
getOption
(
'fullScreen'
))
//
c.setOption('fullScreen', !c.getOption('fullScreen'))
}
,
//
}
,
'Esc'
(
c
)
{
//
'Esc' (c)
{
if
(
c
.
getOption
(
'fullScreen'
))
{
//
if (c.getOption('fullScreen'))
{
c
.
setOption
(
'fullScreen'
,
false
)
//
c.setOption('fullScreen', false)
}
//
}
}
,
//
}
,
[
`${CtrlKey
}
-S`
]
(
c
)
{
//
[`$
{
CtrlKey
}
-
S
`] (c) {
// save()
//
// save()
return
false
//
return false
}
,
//
}
,
[
`${CtrlKey
}
-B`
]
(
c
)
{
//
[`
$
{
CtrlKey
}
-
B
`] (c) {
toggleMarkup
({
start
:
'**'
}
)
//
toggleMarkup({ start: '**'
}
)
return
false
//
return false
}
,
//
}
,
[
`${CtrlKey
}
-I`
]
(
c
)
{
//
[`
$
{
CtrlKey
}
-
I
`] (c) {
toggleMarkup
({
start
:
'*'
}
)
//
toggleMarkup({ start: '*'
}
)
return
false
//
return false
}
,
//
}
,
[
`${CtrlKey
}
-Alt-Right`
]
(
c
)
{
//
[`
$
{
CtrlKey
}
-
Alt
-
Right
`] (c) {
let
lvl
=
getHeaderLevel
(
c
)
//
let lvl = getHeaderLevel(c)
if
(
lvl
>=
6
)
{
lvl
=
5
}
//
if (lvl >= 6) { lvl = 5
}
setHeaderLine
(
lvl
+
1
)
//
setHeaderLine(lvl + 1)
return
false
//
return false
}
,
//
}
,
[
`${CtrlKey
}
-Alt-Left`
]
(
c
)
{
//
[`
$
{
CtrlKey
}
-
Alt
-
Left
`] (c) {
let
lvl
=
getHeaderLevel
(
c
)
//
let lvl = getHeaderLevel(c)
if
(
lvl
<=
1
)
{
lvl
=
2
}
//
if (lvl <= 1) { lvl = 2
}
setHeaderLine
(
lvl
-
1
)
//
setHeaderLine(lvl - 1)
return
false
//
return false
}
//
}
}
//
}
cm
.
value
.
setOption
(
'extraKeys'
,
keyBindings
)
//
cm.value.setOption('extraKeys', keyBindings)
// this.cm.on('inputRead', this.autocomplete)
// this.cm.on('inputRead', this.autocomplete)
// // Handle cursor movement
// // Handle cursor movement
...
@@ -516,11 +576,11 @@ onMounted(async () => {
...
@@ -516,11 +576,11 @@ onMounted(async () => {
// this.cm.on('paste', this.onCmPaste)
// this.cm.on('paste', this.onCmPaste)
// // Render initial preview
// // Render initial preview
processContent
(
pageStore
.
content
)
//
processContent(pageStore.content)
nextTick
(()
=>
{
//
nextTick(() => {
cm
.
value
.
refresh
()
//
cm.value.refresh()
cm
.
value
.
focus
()
//
cm.value.focus()
}
)
//
}
)
EVENT_BUS.on('insertAsset', insertAssetClb)
EVENT_BUS.on('insertAsset', insertAssetClb)
...
@@ -589,6 +649,10 @@ $editor-height-mobile: calc(100vh - 112px - 16px);
...
@@ -589,6 +649,10 @@ $editor-height-mobile: calc(100vh - 112px - 16px);
// @include until($tablet) {
// @include until($tablet) {
// height: $editor-height-mobile;
// height: $editor-height-mobile;
//
}
//
}
> div {
height: 100%;
}
}
}
&-type {
&-type {
writing-mode: vertical-rl;
writing-mode: vertical-rl;
...
@@ -693,7 +757,7 @@ $editor-height-mobile: calc(100vh - 112px - 16px);
...
@@ -693,7 +757,7 @@ $editor-height-mobile: calc(100vh - 112px - 16px);
}
}
&-toolbar {
&-toolbar {
background-color: $primary;
background-color: $primary;
border-left:
4
0px solid darken($primary, 5%);
border-left:
5
0px solid darken($primary, 5%);
color: #FFF;
color: #FFF;
height: 32px;
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