mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-09-28 04:32:58 +00:00
better make link, better makespan make div make space and make nsb
This commit is contained in:
@@ -31,8 +31,7 @@ export async function formatCSS(view) {
|
|||||||
//Prettier can't do it by default, this is crude but it works
|
//Prettier can't do it by default, this is crude but it works
|
||||||
formatted = formatted.replace(
|
formatted = formatted.replace(
|
||||||
/([^{]+)\{\s*\n\s*([^;\n]+:[^;\n]+;)\s*\n\s*\}(\s*)/g,
|
/([^{]+)\{\s*\n\s*([^;\n]+:[^;\n]+;)\s*\n\s*\}(\s*)/g,
|
||||||
(_, selector, decl, whitespace) =>
|
(_, selector, decl, whitespace)=>`${selector} { ${decl.trim()} }${whitespace}`
|
||||||
`${selector} { ${decl.trim()} }${whitespace}`
|
|
||||||
);
|
);
|
||||||
if(formatted === code) return true;
|
if(formatted === code) return true;
|
||||||
|
|
||||||
@@ -57,6 +56,13 @@ export async function formatCSS(view) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const forEachSelection = (view, fn) => {
|
||||||
|
view.dispatch(view.state.changeByRange((range) => fn(view.state, range)));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const insertTab = (view)=>{
|
const insertTab = (view)=>{
|
||||||
// If any selection spans multiple lines, delegates to CodeMirror's indentMore
|
// If any selection spans multiple lines, delegates to CodeMirror's indentMore
|
||||||
// Otherwise inserts two spaces at each cursor/selection
|
// Otherwise inserts two spaces at each cursor/selection
|
||||||
@@ -127,12 +133,12 @@ const wrapSelection = (prefix, suffix) => (view) => {
|
|||||||
{
|
{
|
||||||
from : from,
|
from : from,
|
||||||
to : from + prefix.length,
|
to : from + prefix.length,
|
||||||
insert: ""
|
insert : ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from : to - suffix.length,
|
from : to - suffix.length,
|
||||||
to : to,
|
to : to,
|
||||||
insert: ""
|
insert : ''
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
range : EditorSelection.range(
|
range : EditorSelection.range(
|
||||||
@@ -150,7 +156,7 @@ const wrapSelection = (prefix, suffix) => (view) => {
|
|||||||
},
|
},
|
||||||
range : EditorSelection.range(
|
range : EditorSelection.range(
|
||||||
from,
|
from,
|
||||||
to + suffix.length*2
|
to + suffix.length + prefix.length
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@@ -159,85 +165,111 @@ const wrapSelection = (prefix, suffix) => (view) => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const makeNbsp = (view)=>{
|
const makeNbsp = (view) => forEachSelection(view, (state, range) => {
|
||||||
const { from } = view.state.selection.main;
|
const { from, to } = range;
|
||||||
|
|
||||||
const prev2 = from >= 2
|
|
||||||
? view.state.doc.sliceString(from - 2, from)
|
|
||||||
: '';
|
|
||||||
|
|
||||||
|
const prev2 = from >= 2 ? state.doc.sliceString(from - 2, from) : '';
|
||||||
const insert = (prev2 === ':>' || prev2 === '>>') ? '>' : ':>';
|
const insert = (prev2 === ':>' || prev2 === '>>') ? '>' : ':>';
|
||||||
|
|
||||||
view.dispatch({
|
return {
|
||||||
changes : { from, to: from, insert },
|
changes : { from, to, insert },
|
||||||
selection : { anchor: from + insert.length },
|
range : EditorSelection.cursor(from + insert.length)
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
const makeSpace = (view) => forEachSelection(view, (state, range) => {
|
||||||
};
|
const { from, to } = range;
|
||||||
|
const selected = state.doc.sliceString(from, to);
|
||||||
const makeSpace = (view)=>{
|
|
||||||
const { from, to } = view.state.selection.main;
|
|
||||||
const selected = view.state.doc.sliceString(from, to);
|
|
||||||
const match = selected.match(/^{{width:(\d+)% }}$/);
|
const match = selected.match(/^{{width:(\d+)% }}$/);
|
||||||
let newText = '{{width:10% }}';
|
|
||||||
|
let insert = '{{width:10% }}';
|
||||||
|
|
||||||
if(match) {
|
if(match) {
|
||||||
const percent = Math.min(parseInt(match[1], 10) + 10, 100);
|
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 { from, to } = view.state.selection.main;
|
||||||
const selected = view.state.doc.sliceString(from, to);
|
const selected = view.state.doc.sliceString(from, to);
|
||||||
const match = selected.match(/^{{width:(\d+)% }}$/);
|
const match = selected.match(/^{{width:(\d+)% }}$/);
|
||||||
if(match) {
|
if(match) {
|
||||||
const percent = parseInt(match[1], 10) - 10;
|
const percent = parseInt(match[1], 10) - 10;
|
||||||
const newText = percent > 0 ? `{{width:${percent}% }}` : '';
|
const newText = percent > 0 ? `{{width:${percent}% }}` : '';
|
||||||
view.dispatch({ changes: { from, to, insert: newText } });
|
return{ changes: { from, to, insert: newText } };
|
||||||
}
|
}
|
||||||
return true;
|
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 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 { from, to } = view.state.selection.main;
|
||||||
const selected = view.state.doc.sliceString(from, to).trim();
|
const selected = view.state.doc.sliceString(from, to).trim();
|
||||||
|
|
||||||
const isLink = /^\[(.*)\]\((.*)\)$/.exec(selected);
|
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;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -326,9 +358,9 @@ export const markdownKeymap = Prec.highest(keymap.of([
|
|||||||
{ key: 'Mod-.', run: makeNbsp },
|
{ key: 'Mod-.', run: makeNbsp },
|
||||||
{ key: 'Shift-Mod-.', run: makeSpace },
|
{ key: 'Shift-Mod-.', run: makeSpace },
|
||||||
{ key: 'Shift-Mod-,', run: removeSpace },
|
{ key: 'Shift-Mod-,', run: removeSpace },
|
||||||
{ key: 'Mod-m', run: makeSpan },
|
{ key: 'Mod-m', run: wrapSelection('{{', '}}') },
|
||||||
{ key: 'Shift-Mod-m', run: makeDiv },
|
{ key: 'Shift-Mod-m', run: wrapSelection('{{\n', '\n}}') },
|
||||||
{ key: 'Mod-/', run: makeComment },
|
{ key: 'Mod-/', run: wrapSelection('<!-- ', ' -->') },
|
||||||
{ key: 'Mod-k', run: makeLink },
|
{ key: 'Mod-k', run: makeLink },
|
||||||
{ key: 'Mod-Shift-u', run: makeList('UL') },
|
{ key: 'Mod-Shift-u', run: makeList('UL') },
|
||||||
{ key: 'Mod-Shift-o', run: makeList('OL') },
|
{ key: 'Mod-Shift-o', run: makeList('OL') },
|
||||||
|
|||||||
Reference in New Issue
Block a user