Skip to content

Commit

Permalink
feat: validation results output to clipboard + save to file + fix hyp…
Browse files Browse the repository at this point in the history
…henation check
  • Loading branch information
NGPixel committed Feb 7, 2025
1 parent d2e09d6 commit 9492048
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 20 deletions.
1 change: 1 addition & 0 deletions src-electron/electron-preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ contextBridge.exposeInMainWorld('ipcBridge', {
gitDiscardChanges: (files) => ipcRenderer.invoke('gitDiscardChanges', { files }),
gitCommit: (message) => ipcRenderer.invoke('gitCommit', { message }),
lspSendRequest: (method, params) => ipcRenderer.invoke('lspSendRequest', { method, params }),
saveValidationResults: (output) => ipcRenderer.invoke('saveValidationResults', { output }),
persistSession: (data) => ipcRenderer.invoke('persistSession', data),
restoreSession: () => ipcRenderer.invoke('restoreSession')
})
24 changes: 24 additions & 0 deletions src-electron/handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,30 @@ export function registerCallbacks (mainWindow, mainMenu, auth, git, lsp, tlm) {
lsp.sendNotification(opts.method, opts.params)
})
// ----------------------------------------------------------
// VALIDATION CHECKS
// ----------------------------------------------------------
ipcMain.handle('saveValidationResults', async (ev, opts) => {
const saveOpts = await dialog.showSaveDialog(mainWindow, {
title: 'Save Validation Results As...',
defaultPath: path.join(app.getPath('desktop'), 'results.txt'),
filters: [{
name: 'Plain Text',
extensions: ['txt']
}],
properties: ['showOverwriteConfirmation', 'createDirectory']
})
if (!saveOpts.canceled) {
try {
await fs.writeFile(saveOpts.filePath, opts.output)
return true
} catch (err) {
console.error(err)
throw err
}
}
return false
})
// ----------------------------------------------------------
// MISC
// ----------------------------------------------------------
ipcMain.on('setMenuItemCheckedState', (ev, opts) => {
Expand Down
72 changes: 65 additions & 7 deletions src/components/DrawerChecks.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,52 @@ q-list
q-icon(v-else-if='editorStore.validationChecks[chk.key] === -1' name='mdi-close-circle' size='xs' color='red-5')
q-icon(v-else-if='editorStore.validationChecks[chk.key] === -2' name='mdi-alert-circle' size='xs' color='orange-5')
q-expansion-item.bg-dark-5(
v-if='editorStore.validationChecksDetails[chk.key].length > 0'
v-if='editorStore.validationChecksDetails[chk.key].count > 0'
group='checks'
dense
@show='setCurrentCheck(chk.key)'
)
template(#header)
q-item-section.q-pl-sm
.flex.items-center
q-item-label.text-purple-2 └─ {{ editorStore.validationChecksDetails[chk.key].length }} issues
q-item-label.text-purple-2 └─ {{ editorStore.validationChecksDetails[chk.key].count }} issues
.bg-dark-5.checkdetails
q-list(dense, separator)
q-item
q-item-section
.flex
q-btn.q-mr-sm(
label='Reset Ignores'
padding='none xs'
size='sm'
no-caps
outline
color='purple-3'
disabled
)
q-space
q-btn.q-mr-sm(
label='Save to File'
padding='none xs'
size='sm'
no-caps
outline
color='purple-3'
@click='saveResultsToFile(chk.key)'
:disabled='!editorStore.validationChecksDetails[chk.key].hasTextOutput'
)
q-btn(
label='Copy to Clipboard'
padding='none xs'
size='sm'
no-caps
outline
color='purple-3'
@click='copyResultsToClipboard(chk.key)'
:disabled='!editorStore.validationChecksDetails[chk.key].hasTextOutput'
)
q-item(
v-for='dtl of editorStore.validationChecksDetails[chk.key]'
v-for='dtl of editorStore.validationChecksDetails[chk.key].details'
:key='dtl.key'
clickable
@click='goToPosition(dtl.range)'
Expand Down Expand Up @@ -139,7 +173,7 @@ function articlesCheck (silent) {
}
} else {
editorStore.setValidationCheckState('articles', -2)
editorStore.setValidationCheckDetails('articles', results.details)
editorStore.setValidationCheckDetails('articles', results)
}
}
Expand All @@ -158,7 +192,7 @@ function hyphenationCheck (silent = false) {
}
} else {
editorStore.setValidationCheckState('hyphenation', -2)
editorStore.setValidationCheckDetails('hyphenation', results.details)
editorStore.setValidationCheckDetails('hyphenation', results)
}
}
Expand All @@ -177,7 +211,7 @@ function inclusiveLangCheck (silent = false) {
}
} else {
editorStore.setValidationCheckState('inclusiveLanguage', -2)
editorStore.setValidationCheckDetails('inclusiveLanguage', results.details)
editorStore.setValidationCheckDetails('inclusiveLanguage', results)
}
}
Expand Down Expand Up @@ -213,7 +247,7 @@ function placeholdersCheck (silent = false) {
}
} else {
editorStore.setValidationCheckState('placeholders', -2)
editorStore.setValidationCheckDetails('placeholders', results.details)
editorStore.setValidationCheckDetails('placeholders', results)
}
}
Expand Down Expand Up @@ -275,6 +309,30 @@ function setCurrentCheck (key) {
editorStore.validationChecksCurrent = key
}
// Validation Results Output
async function saveResultsToFile (key) {
if (await window.ipcBridge.saveValidationResults(editorStore.validationChecksDetails[key].getTextOutput())) {
$q.notify({
message: 'Results saved!',
caption: 'Results have been saved to file successfully.',
color: 'positive',
icon: 'mdi-content-save-check-outline'
})
}
}
function copyResultsToClipboard (key) {
window.ipcBridge.emit('writeToClipboard', {
text: editorStore.validationChecksDetails[key].getTextOutput()
})
$q.notify({
message: 'Results copied!',
caption: 'Results have been copied to the clipboard successfully.',
color: 'positive',
icon: 'mdi-clipboard'
})
}
// MOUNTED
onMounted(() => {
Expand Down
25 changes: 24 additions & 1 deletion src/tools/articles.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ export function checkArticles (text) {

const decorations = []
const details = []
const termCount = {}

function addToTermCount (term) {
const termSanitized = term.trim()
if (termCount[termSanitized]) {
termCount[termSanitized]++
} else {
termCount[termSanitized] = 1
}
}

for (const [lineIdx, line] of textLines.entries()) {
for (const match of line.matchAll(partARgx)) {
decorations.push({
Expand Down Expand Up @@ -41,6 +52,7 @@ export function checkArticles (text) {
endColumn: match.index + match[0].length
}
})
addToTermCount(match[0])
}
for (const match of line.matchAll(partARRgx)) {
decorations.push({
Expand Down Expand Up @@ -71,6 +83,7 @@ export function checkArticles (text) {
endColumn: match.index + match[0].length
}
})
addToTermCount(match[0])
}
for (const match of line.matchAll(partBRgx)) {
decorations.push({
Expand Down Expand Up @@ -101,6 +114,7 @@ export function checkArticles (text) {
endColumn: match.index + match[0].length
}
})
addToTermCount(match[0])
}
for (const match of line.matchAll(partCRgx)) {
decorations.push({
Expand Down Expand Up @@ -131,6 +145,7 @@ export function checkArticles (text) {
endColumn: match.index + match[0].length
}
})
addToTermCount(match[0])
}
for (const match of line.matchAll(partCLFRgx)) {
decorations.push({
Expand Down Expand Up @@ -161,13 +176,21 @@ export function checkArticles (text) {
endColumn: match.index + match[0].length
}
})
addToTermCount(match[0])
}
}

decorationsStore.get('articles').set(decorations)

return {
count: decorations.length,
details: sortBy(details, d => d.range.startLineNumber)
details: sortBy(details, d => d.range.startLineNumber),
hasTextOutput: true,
getTextOutput: () => {
return `Articles
-------------
${Object.entries(termCount).map(([k, v]) => `${k} (${v})`).join('\n')}
`
}
}
}
40 changes: 30 additions & 10 deletions src/tools/hyphenation.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ export function checkHyphenation (text) {
const occurences = []
const hyphenTerms = []
const hyphenTermsOccurences = []
const termCount = {}
for (const [lineIdx, line] of textLines.entries()) {
for (const match of line.matchAll(hyphenTermRgx)) {
if (match[0].length > 3) {
if (!hyphenTerms.includes(match[0])) {
hyphenTerms.push(match[0])
const termLower = match[0].toLowerCase()
if (!hyphenTerms.includes(termLower)) {
hyphenTerms.push(termLower)
}
hyphenTermsOccurences.push({
term: match[0],
term: termLower,
range: {
startLineNumber: lineIdx + 1,
startColumn: match.index + 1,
Expand All @@ -39,16 +41,17 @@ export function checkHyphenation (text) {
for (const [lineIdx, line] of textLines.entries()) {
for (const term of hyphenTerms) {
const altTerm = term.replaceAll('-', '')
const altTermRgx = new RegExp(`(?:^|[>" ])${altTerm}(?:[. "<]|$)`, 'gi')
const altTermRgx = new RegExp(`(?:^|[>" ])(${altTerm})(?:[. "<]|$)`, 'gi')
for (const match of line.matchAll(altTermRgx)) {
const matchLower = match[1].toLowerCase()
let occIdx = occurences.indexOf(term)
if (occIdx < 0) {
occIdx = occurences.push(term) - 1
for (const termOcc of hyphenTermsOccurences.filter(t => t.term === term)) {
decorations.push({
options: {
hoverMessage: {
value: `[${occIdx}] Inconsistent Hyphenation (Alternate of ${altTerm})`
value: `[${occIdx + 1}] Inconsistent Hyphenation (Alternate of ${altTerm})`
},
className: 'dec-warning',
minimap: {
Expand All @@ -61,15 +64,20 @@ export function checkHyphenation (text) {
details.push({
key: crypto.randomUUID(),
group: occIdx + 1,
message: `${term} is alternate of ${altTerm}`,
message: `${term} has alternate term(s)`,
range: termOcc.range
})
}
if (termCount[term]) {
termCount[term]++
} else {
termCount[term] = 1
}
}
decorations.push({
options: {
hoverMessage: {
value: `[${occIdx}] Inconsistent Hyphenation (Alternate of ${term})`
value: `[${occIdx + 1}] Inconsistent Hyphenation (Alternate of ${term})`
},
className: 'dec-warning',
minimap: {
Expand All @@ -87,14 +95,19 @@ export function checkHyphenation (text) {
details.push({
key: crypto.randomUUID(),
group: occIdx + 1,
message: `${term} has alternate term(s)`,
message: `${matchLower} is alternate of ${term}`,
range: {
startLineNumber: lineIdx + 1,
startColumn: match.index + 2,
endLineNumber: lineIdx + 1,
endColumn: match.index + match[0].length
}
})
if (termCount[matchLower]) {
termCount[matchLower]++
} else {
termCount[matchLower] = 1
}
}
}
}
Expand All @@ -103,7 +116,14 @@ export function checkHyphenation (text) {
decorationsStore.get('hyphenation').set(decorations)

return {
count: occurences.length,
details: sortBy(details, d => d.range.startLineNumber)
count: details.length,
details: sortBy(details, d => d.range.startLineNumber),
hasTextOutput: true,
getTextOutput: () => {
return `Hyphenation
-------------
${Object.entries(termCount).map(([k, v]) => `${k} (${v})`).join('\n')}
`
}
}
}
15 changes: 14 additions & 1 deletion src/tools/inclusive-language.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export function checkInclusiveLanguage (text) {
const decorations = []
const occurences = []
const details = []
const termCount = {}
for (const [lineIdx, line] of textLines.entries()) {
for (const match of line.matchAll(matchRgx)) {
const term = match[1].toLowerCase()
Expand Down Expand Up @@ -80,13 +81,25 @@ export function checkInclusiveLanguage (text) {
endColumn: match.index + 1 + match[0].length
}
})
if (termCount[term]) {
termCount[term]++
} else {
termCount[term] = 1
}
}
}

decorationsStore.get('inclusiveLanguage').set(decorations)

return {
count: decorations.length,
details: sortBy(details, d => d.range.startLineNumber)
details: sortBy(details, d => d.range.startLineNumber),
hasTextOutput: true,
getTextOutput: () => {
return `Inclusive Language
-------------
${Object.entries(termCount).map(([k, v]) => `${k} (${v})`).join('\n')}
`
}
}
}
Loading

0 comments on commit 9492048

Please sign in to comment.