From ffa15f0935a83bc77d20f9e59d3bb1c6ee30cee5 Mon Sep 17 00:00:00 2001 From: Nicolas Giard Date: Tue, 28 Jan 2025 23:11:49 -0500 Subject: [PATCH] feat: common placeholders validation check --- src/components/DrawerChecks.vue | 27 ++++++++++++++++ src/stores/editor.js | 6 ++-- src/tools/placeholders.js | 57 +++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 src/tools/placeholders.js diff --git a/src/components/DrawerChecks.vue b/src/components/DrawerChecks.vue index b46acf5..1eab2ce 100644 --- a/src/components/DrawerChecks.vue +++ b/src/components/DrawerChecks.vue @@ -69,6 +69,7 @@ import { checkArticles } from 'src/tools/articles' import { checkHyphenation } from 'src/tools/hyphenation' import { checkInclusiveLanguage } from 'src/tools/inclusive-language' import { checkNonAscii } from 'src/tools/non-ascii' +import { checkCommonPlaceholders } from 'src/tools/placeholders' import { useDocsStore } from 'src/stores/docs' import { useEditorStore } from 'src/stores/editor' import { modelStore } from 'src/stores/models' @@ -108,6 +109,13 @@ const valChecks = [ description: 'Check for non-ASCII characters', icon: 'mdi-translate', click: () => nonAsciiCheck() + }, + { + key: 'placeholders', + title: 'Placeholders Check', + description: 'Check for common placeholders', + icon: 'mdi-select-remove', + click: () => placeholdersCheck() } ] @@ -187,6 +195,25 @@ function nonAsciiCheck (silent = false) { } } +function placeholdersCheck (silent = false) { + const results = checkCommonPlaceholders(modelStore[docsStore.activeDocument.id].getValue()) + if (results.count < 1) { + editorStore.setValidationCheckState('placeholders', 1) + editorStore.setValidationCheckDetails('placeholders', []) + if (!silent) { + $q.notify({ + message: 'Looks good!', + caption: 'No common placeholders found.', + color: 'positive', + icon: 'mdi-select-remove' + }) + } + } else { + editorStore.setValidationCheckState('placeholders', -2) + editorStore.setValidationCheckDetails('placeholders', results.details) + } +} + function runAllChecks () { editorStore.clearErrors() articlesCheck(true) diff --git a/src/stores/editor.js b/src/stores/editor.js index da9a679..3920f64 100644 --- a/src/stores/editor.js +++ b/src/stores/editor.js @@ -49,13 +49,15 @@ export const useEditorStore = defineStore('editor', { articles: 0, hyphenation: 0, inclusiveLanguage: 0, - nonAscii: 0 + nonAscii: 0, + placeholders: 0 }, validationChecksDetails: { articles: [], hyphenation: [], inclusiveLanguage: [], - nonAscii: [] + nonAscii: [], + placeholders: [] }, wordWrap: true, workingDirectory: '', diff --git a/src/tools/placeholders.js b/src/tools/placeholders.js new file mode 100644 index 0000000..4f42364 --- /dev/null +++ b/src/tools/placeholders.js @@ -0,0 +1,57 @@ +import { sortBy } from 'lodash-es' +import { decorationsStore } from 'src/stores/models' + +export function checkCommonPlaceholders (text) { + const matchRgx = /(?:[^a-z0-9]|RFC)(?TBD|TBA|XX|YY|NN|MM|0000|TODO)(?:[^a-z0-9])/gi + const textLines = text.split('\n') + + const decorations = [] + const occurences = [] + const details = [] + for (const [lineIdx, line] of textLines.entries()) { + for (const match of line.matchAll(matchRgx)) { + const term = match[1].toLowerCase() + const termStartIndex = match[0].indexOf(match[1]) + let occIdx = occurences.indexOf(term) + if (occIdx < 0) { + occIdx = occurences.push(term) - 1 + } + decorations.push({ + options: { + hoverMessage: { + value: `Common placeholder term ${match[1]} detected.` + }, + className: 'dec-warning', + minimap: { + position: 1 + }, + glyphMarginClassName: 'dec-warning-margin' + }, + range: { + startLineNumber: lineIdx + 1, + startColumn: match.index + termStartIndex + 1, + endLineNumber: lineIdx + 1, + endColumn: match.index + termStartIndex + 1 + match[1].length + } + }) + details.push({ + key: crypto.randomUUID(), + group: occIdx + 1, + message: match[1].toLowerCase(), + range: { + startLineNumber: lineIdx + 1, + startColumn: match.index + termStartIndex + 1, + endLineNumber: lineIdx + 1, + endColumn: match.index + termStartIndex + 1 + match[1].length + } + }) + } + } + + decorationsStore.get('placeholders').set(decorations) + + return { + count: decorations.length, + details: sortBy(details, d => d.range.startLineNumber) + } +}