-
Notifications
You must be signed in to change notification settings - Fork 103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Upgrade to CodeMirror 6 #2058
base: master
Are you sure you want to change the base?
Upgrade to CodeMirror 6 #2058
Conversation
- upgrade to CM6 - migrated editor line highlighter composable TODO - code completion - test migration
Removed obsolete CSS
@grolu You need rebase this pull request with latest master branch. Please check. |
3d3f503
to
aa0192d
Compare
- Restructured main scss theme dependant classes
Make editor always consume 100% height Cleaned-up some comments
…m:gardener/dashboard into enh/upgrade_cm_6
let [startLine, endLine = startLine] = values | ||
const [startLineCurrent, endLineCurrent] = valuesCurrent | ||
|
||
if (endLine === startLine && startLine === startLineCurrent && !endLineCurrent) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had similar code in my PR where I introduce the feature to clear the selected line if the current selected line is clicked again, however holger propsed to use ESC-key instead to clear the selection. Clearing with ESC-key currently does not work anymore
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know. The problem here is that I did not find a way to add a key listener to the gutter. It is possible to add a key listener to the editor itself and clear the highlight on escape. However, the editor needs to be focused, so you would need to click inside the editor to make this work.
So I checked how Github does the highlighting and it does not clear the selection on escape, it works exactly like I implemented it now.
label: string, | ||
displayLabel: text, | ||
detail: typeText, | ||
type: 'keyword', // TODO check weather we want to show diffrent icons |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a list of icons / types to choose from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://codemirror.net/docs/ref/#autocomplete.Completion.type
It is also possible to omit the type property so that no icon is rendered. AFAIK it is not possible to provide a custom render function for the completion box (like we did for CM5), only for the content that is shown next to it, the info (details) it is possible to provide a render function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's leave it like it is for now (and remove the TODO), I do not have a better suggestion
|
||
return { lineTokens, lineString } | ||
return token |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not part of this PR, but the hasOnlyTypeOrTypeAndFormatProperties
function needs an overhaul
const hasOnlyTypeOrTypeAndFormatProperties = value => {
if (value.every(item => {
return (Object.keys(item).length === 1 && item.type) ||
(Object.keys(item).length === 2 && item.type && item.format)
})) {
return true
}
return false
}
How about refactoring it to either
const hasValidKeys = value => {
return value.every(item => {
const keys = Object.keys(item)
const hasType = 'type' in item
const hasFormat = 'format' in item
return (keys.length === 1 && hasType) || (keys.length === 2 && hasType && hasFormat)
})
}
or
const hasValidKeys = value => {
const validKeyCombinations = [
['type'],
['type', 'format']
]
return value.every(item => {
const keys = Object.keys(item)
return validKeyCombinations.some(combination =>
combination.length === keys.length && combination.every(key => keys.includes(key))
)
})
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you want to change it in this PR anyway?
# Conflicts: # .pnp.cjs # .yarn/cache/codemirror-npm-5.65.18-debbbac10b-806e00c708.zip # frontend/src/composables/useShootEditor/helper.js # frontend/src/composables/useShootEditor/index.js # yarn.lock
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor things, otherwise /lgtm
@@ -83,9 +90,9 @@ export class EditorCompletions { | |||
end, | |||
string, | |||
} | |||
if (token.start <= pos.ch && pos.ch <= token.end) { | |||
if (token.start <= lineChar && lineChar <= token.end) { | |||
// Ensure that mouse pointer is on propety and not somewhere else on this line |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Ensure that mouse pointer is on propety and not somewhere else on this line | |
// Ensure that mouse pointer is on property and not somewhere else on this line |
// Ensure that mouse pointer is on propety and not somewhere else on this line | ||
const completions = this._getYamlCompletions(token, pos, cm, true) | ||
const completions = this._getYamlCompletions(token, { ch: lineChar, line }, cmView, true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extract to variable for better readability
const completions = this._getYamlCompletions(token, { ch: lineChar, line }, cmView, true) | |
const cur = { ch: lineChar, line } | |
const completions = this._getYamlCompletions(token, cur, cmView, true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is cur? Is it current? Or is it cursor? Use easy understable names.
|
||
return { lineTokens, lineString } | ||
return token |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you want to change it in this PR anyway?
<div v-bind="slotProps.props"> | ||
<g-action-button | ||
size="x-small" | ||
:icon="renderWhitespaces ? 'mdi-grid' : 'mdi-grid-off'" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These icons are not intuitive to me. I would use these ones. We should use Hide and Show (not Render). And I think we can delete the tab icon in assets?
<v-tooltip location="top">
<template #activator="slotProps">
<div v-bind="slotProps.props">
<v-btn
variant="text"
size="x-small"
flat
icon
color="black"
@click="renderWhitespaces = !renderWhitespaces"
>
<span v-html="renderWhitespaces ? whitespaceEye : whitespaceEyeOff" style="width: 18px; height: 18px"/>
</v-btn>
</div>
</template>
<span>{{ renderWhitespaces ? 'Hide' : 'Render' }} whitespaces</span>
</v-tooltip>
} | ||
return highlights | ||
}, | ||
provide: f => EditorView.decorations.from(f), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
provide: f => EditorView.decorations.from(f), | |
provide (f) { | |
return EditorView.decorations.from(f) | |
}, |
export function useEditorWhitespace () { | ||
const matchDecorator = new MatchDecorator({ | ||
regexp: /[ \t]/g, | ||
decoration: match => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
decoration: match => { | |
decoration (match) { |
return Decoration.mark({ | ||
attributes: { class: 'g-cm-highlightedTab' }, | ||
}) | ||
} else if (match[0] === ' ') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} else if (match[0] === ' ') { | |
} | |
if (match[0] === ' ') { |
// Ensure that mouse pointer is on propety and not somewhere else on this line | ||
const completions = this._getYamlCompletions(token, pos, cm, true) | ||
const completions = this._getYamlCompletions(token, { ch: lineChar, line }, cmView, true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is cur? Is it current? Or is it cursor? Use easy understable names.
return completionArray | ||
} | ||
|
||
// get token at cursor position in editor with yaml content | ||
_getYamlToken (cm, cur) { | ||
_getYamlToken (cmView, cur) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These methods starting with underscore should be private and the underscore removed.
.v-theme-- { | ||
@mixin alternate-row-background($background-color) { | ||
.alternate-row-background > div:nth-of-type(even) { | ||
background-color: $background-color; | ||
} | ||
} | ||
|
||
@mixin g-highlighted($background-color, $border-color) { | ||
.g-highlighted { | ||
position: relative; | ||
background-color: $background-color; | ||
/* Left border */ | ||
box-shadow: inset 1px 0 0 0 $border-color; | ||
|
||
/* Top border */ | ||
&.g-highlighted--top::before { | ||
content: ''; | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
right: 0; | ||
height: 1px; /* Adjust border thickness as needed */ | ||
background-color: $border-color; | ||
} | ||
|
||
/* Bottom border */ | ||
&.g-highlighted--bottom::after { | ||
content: ''; | ||
position: absolute; | ||
bottom: 0; | ||
left: 0; | ||
right: 0; | ||
height: 1px; /* Adjust border thickness as needed */ | ||
background-color: $border-color; | ||
} | ||
} | ||
} | ||
|
||
@mixin g-cm-highlighted-whitespace($whitespace-color) { | ||
$whitespace-color-encoded: #{'%23' + str-slice(inspect($whitespace-color), 2)}; | ||
|
||
.g-cm-highlightedSpace { | ||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="6" height="6"><circle cx="3" cy="3" r="1" fill="#{$whitespace-color-encoded}" /></svg>'); | ||
background-repeat: no-repeat; | ||
background-position: center; | ||
background-size: contain; | ||
} | ||
|
||
.g-cm-highlightedTab { | ||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="10" height="6"><path d="M0 3 L10 3" stroke="#{$whitespace-color-encoded}" stroke-width="1" /></svg>'); | ||
background-repeat: no-repeat; | ||
background-position: center; | ||
background-size: contain; | ||
} | ||
|
||
.g-cm-newline-marker { | ||
color: $whitespace-color; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.v-theme-- { | |
@mixin alternate-row-background($background-color) { | |
.alternate-row-background > div:nth-of-type(even) { | |
background-color: $background-color; | |
} | |
} | |
@mixin g-highlighted($background-color, $border-color) { | |
.g-highlighted { | |
position: relative; | |
background-color: $background-color; | |
/* Left border */ | |
box-shadow: inset 1px 0 0 0 $border-color; | |
/* Top border */ | |
&.g-highlighted--top::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
height: 1px; /* Adjust border thickness as needed */ | |
background-color: $border-color; | |
} | |
/* Bottom border */ | |
&.g-highlighted--bottom::after { | |
content: ''; | |
position: absolute; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
height: 1px; /* Adjust border thickness as needed */ | |
background-color: $border-color; | |
} | |
} | |
} | |
@mixin g-cm-highlighted-whitespace($whitespace-color) { | |
$whitespace-color-encoded: #{'%23' + str-slice(inspect($whitespace-color), 2)}; | |
.g-cm-highlightedSpace { | |
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="6" height="6"><circle cx="3" cy="3" r="1" fill="#{$whitespace-color-encoded}" /></svg>'); | |
background-repeat: no-repeat; | |
background-position: center; | |
background-size: contain; | |
} | |
.g-cm-highlightedTab { | |
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="10" height="6"><path d="M0 3 L10 3" stroke="#{$whitespace-color-encoded}" stroke-width="1" /></svg>'); | |
background-repeat: no-repeat; | |
background-position: center; | |
background-size: contain; | |
} | |
.g-cm-newline-marker { | |
color: $whitespace-color; | |
} | |
} | |
@mixin alternate-row-background($background-color) { | |
.alternate-row-background>div:nth-of-type(even) { | |
background-color: $background-color; | |
} | |
} | |
@mixin g-highlighted($background-color, $border-color) { | |
.g-highlighted { | |
position: relative; | |
background-color: $background-color; | |
/* Left border */ | |
box-shadow: inset 1px 0 0 0 $border-color; | |
/* Top border */ | |
&.g-highlighted--top::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
height: 1px; | |
/* Adjust border thickness as needed */ | |
background-color: $border-color; | |
} | |
/* Bottom border */ | |
&.g-highlighted--bottom::after { | |
content: ''; | |
position: absolute; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
height: 1px; | |
/* Adjust border thickness as needed */ | |
background-color: $border-color; | |
} | |
} | |
} | |
@mixin g-cm-highlighted-whitespace($whitespace-color) { | |
$whitespace-color-encoded: #{'%23' + str-slice(inspect($whitespace-color), 2)}; | |
.g-cm-highlightedSpace { | |
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="6" height="6"><circle cx="3" cy="3" r="1" fill="#{$whitespace-color-encoded}" /></svg>'); | |
background-repeat: no-repeat; | |
background-position: center; | |
background-size: contain; | |
} | |
.g-cm-highlightedTab { | |
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="10" height="6"><path d="M0 3 L10 3" stroke="#{$whitespace-color-encoded}" stroke-width="1" /></svg>'); | |
background-repeat: no-repeat; | |
background-position: center; | |
background-size: contain; | |
} | |
.g-cm-newline-marker { | |
color: $whitespace-color; | |
} | |
} | |
.v-theme-- { |
} | ||
return next() | ||
}) | ||
onBeforeRouteUpdate(async (to, from, next) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason for this change. I think this has nothing to do with cm6. Isnt't this part of an ohter PR?
What this PR does / why we need it:
Upgrade CodeMirror 5 to CodeMirror 6
Which issue(s) this PR fixes:
Fixes #1892
Special notes for your reviewer:
Release note: