better make link, better makespan make div make space and make nsb

This commit is contained in:
Víctor Losada Hernández
2026-09-27 22:24:44 +02:00
parent cf83806759
commit a39b86edf0
@@ -31,8 +31,7 @@ export async function formatCSS(view) {
//Prettier can't do it by default, this is crude but it works
formatted = formatted.replace(
/([^{]+)\{\s*\n\s*([^;\n]+:[^;\n]+;)\s*\n\s*\}(\s*)/g,
(_, selector, decl, whitespace) =>
`${selector} { ${decl.trim()} }${whitespace}`
(_, selector, decl, whitespace)=>`${selector} { ${decl.trim()} }${whitespace}`
);
if(formatted === code) return true;
@@ -57,6 +56,13 @@ export async function formatCSS(view) {
return true;
}
const forEachSelection = (view, fn) => {
view.dispatch(view.state.changeByRange((range) => fn(view.state, range)));
return true;
};
const insertTab = (view)=>{
// If any selection spans multiple lines, delegates to CodeMirror's indentMore
// Otherwise inserts two spaces at each cursor/selection
@@ -127,12 +133,12 @@ const wrapSelection = (prefix, suffix) => (view) => {
{
from : from,
to : from + prefix.length,
insert: ""
insert : ''
},
{
from : to - suffix.length,
to : to,
insert: ""
insert : ''
}
],
range : EditorSelection.range(
@@ -150,7 +156,7 @@ const wrapSelection = (prefix, suffix) => (view) => {
},
range : EditorSelection.range(
from,
to + suffix.length*2
to + suffix.length + prefix.length
)
};
})
@@ -159,85 +165,111 @@ const wrapSelection = (prefix, suffix) => (view) => {
return true;
};
const makeNbsp = (view)=>{
const { from } = view.state.selection.main;
const prev2 = from >= 2
? view.state.doc.sliceString(from - 2, from)
: '';
const makeNbsp = (view) => forEachSelection(view, (state, range) => {
const { from, to } = range;
const prev2 = from >= 2 ? state.doc.sliceString(from - 2, from) : '';
const insert = (prev2 === ':>' || prev2 === '>>') ? '>' : ':>';
view.dispatch({
changes : { from, to: from, insert },
selection : { anchor: from + insert.length },
return {
changes : { from, to, insert },
range : EditorSelection.cursor(from + insert.length)
};
});
return true;
};
const makeSpace = (view)=>{
const { from, to } = view.state.selection.main;
const selected = view.state.doc.sliceString(from, to);
const makeSpace = (view) => forEachSelection(view, (state, range) => {
const { from, to } = range;
const selected = state.doc.sliceString(from, to);
const match = selected.match(/^{{width:(\d+)% }}$/);
let newText = '{{width:10% }}';
let insert = '{{width:10% }}';
if(match) {
const percent = Math.min(parseInt(match[1], 10) + 10, 100);
newText = `{{width:${percent}% }}`;
insert = `{{width:${percent}% }}`;
}
view.dispatch({ changes: { from, to, insert: newText } });
return true;
};
const removeSpace = (view)=>{
return {
changes : { from, to, insert },
range : EditorSelection.range(from, from + insert.length)
};
});
const removeSpace = (view) => forEachSelection(view, (state, range) => {
const { from, to } = view.state.selection.main;
const selected = view.state.doc.sliceString(from, to);
const match = selected.match(/^{{width:(\d+)% }}$/);
if(match) {
const percent = parseInt(match[1], 10) - 10;
const newText = percent > 0 ? `{{width:${percent}% }}` : '';
view.dispatch({ changes: { from, to, insert: newText } });
return{ changes: { from, to, insert: newText } };
}
return true;
};
const makeSpan = (view)=>{
const { from, to } = view.state.selection.main;
const selected = view.state.doc.sliceString(from, to);
const text = selected.startsWith('{{') && selected.endsWith('}}')
? selected.slice(2, -2)
: `{{${selected}}}`;
view.dispatch({ changes: { from, to, insert: text } });
return true;
};
const makeDiv = (view)=>{
const { from, to } = view.state.selection.main;
const selected = view.state.doc.sliceString(from, to);
const text = selected.startsWith('{{') && selected.endsWith('}}')
? selected.slice(2, -2)
: `{{\n${selected}\n}}`;
view.dispatch({ changes: { from, to, insert: text } });
return true;
};
const makeComment = (view)=>{
const { from, to } = view.state.selection.main;
const selected = view.state.doc.sliceString(from, to);
const isHtmlComment = selected.startsWith('<!--') && selected.endsWith('-->');
const text = isHtmlComment
? selected.slice(4, -3)
: `<!-- ${selected} -->`;
view.dispatch({ changes: { from, to, insert: text } });
return true;
};
});
const makeLink = (view) => {
const ranges = view.state.selection.ranges;
// Multiple selections: pair them as alt text + URL
if (ranges.length > 1) {
const changes = [];
for (let i = 0; i < ranges.length - 1; i += 2) {
const alt = view.state.doc.sliceString(
ranges[i].from,
ranges[i].to
).trim();
const url = view.state.doc.sliceString(
ranges[i + 1].from,
ranges[i + 1].to
).trim();
changes.push({
from: ranges[i].from,
to: ranges[i + 1].to,
insert: `[${alt}](${url})`
});
}
view.dispatch({ changes });
return true;
}
// Single selection: existing behavior
const { from, to } = view.state.selection.main;
const selected = view.state.doc.sliceString(from, to).trim();
const isLink = /^\[(.*)\]\((.*)\)$/.exec(selected);
const text = isLink ? `${isLink[1]} ${isLink[2]}` : `[${selected || 'alt text'}](url)`;
view.dispatch({ changes: { from, to, insert: text } });
let text;
if (isLink) {
text = `${isLink[1]} ${isLink[2]}`;
} else {
const isUrl =
/^(https?:\/\/|www\.)\S+$/i.test(selected) ||
/^[a-z0-9-]+(\.[a-z0-9-]+)+([/?#]\S*)?$/i.test(selected);
if (isUrl) {
const url = selected;
const domain = url
.replace(/^https?:\/\//i, '')
.replace(/^www\./i, '')
.split(/[/?#]/)[0];
const name = domain.split('.')[0];
text = `[${name}](${url})`;
} else {
text = `[${selected || 'alt text'}](url)`;
}
}
view.dispatch({
changes: { from, to, insert: text }
});
return true;
};
@@ -326,9 +358,9 @@ export const markdownKeymap = Prec.highest(keymap.of([
{ key: 'Mod-.', run: makeNbsp },
{ key: 'Shift-Mod-.', run: makeSpace },
{ key: 'Shift-Mod-,', run: removeSpace },
{ key: 'Mod-m', run: makeSpan },
{ key: 'Shift-Mod-m', run: makeDiv },
{ key: 'Mod-/', run: makeComment },
{ key: 'Mod-m', run: wrapSelection('{{', '}}') },
{ key: 'Shift-Mod-m', run: wrapSelection('{{\n', '\n}}') },
{ key: 'Mod-/', run: wrapSelection('<!-- ', ' -->') },
{ key: 'Mod-k', run: makeLink },
{ key: 'Mod-Shift-u', run: makeList('UL') },
{ key: 'Mod-Shift-o', run: makeList('OL') },