import * as vscode from 'vscode' ; import * as path from 'path' ; import * as util from './util' ; import * as tableEditor from './tableEditor' ; export async function insertRelPath ( pathTo , withExt ) { const editor = vscode . window . activeTextEditor ; if ( ! editor ) { return ; } let pathFrom = editor . document . uri . fsPath ; pathFrom = path . dirname ( pathFrom ) ; let relPath = path . relative ( pathFrom , pathTo ) ; relPath = relPath . replace ( /\\/g , '/' ) ; if ( ! withExt ) { const parse = path . parse ( relPath ) ; if ( parse . dir ) { relPath = `${ parse . dir }/${ parse . name }` ; } else { relPath = parse . name ; } } await editor . edit ( editBuilder => { editBuilder . replace ( editor . selection , relPath ) ; } ) ; const selectionEnd = editor . selection . end ; editor . selection = new vscode . Selection ( selectionEnd , selectionEnd ) ; vscode . window . showTextDocument ( editor . document ) ; } export async function bold ( ) { const removed = await _removeDecoChar ( "bold" ) ; if ( ! removed ) { _addDecorationChar ( "**" ) ; } } export async function italic ( ) { const removed = await _removeDecoChar ( "italic" ) ; if ( ! removed ) { _addDecorationChar ( "*" ) ; } } export async function inlineRaw ( ) { const removed = await _removeDecoChar ( "inlineRaw" ) ; if ( ! removed ) { _addDecorationChar ( "``" ) ; } } async function _addDecorationChar ( decoChar ) { const editor = vscode . window . activeTextEditor ; if ( ! editor ) { return ; } const document = editor . document ; const selections = editor . selections ; let insertTextList = [ ] ; for ( const selection of selections ) { const selBeforeChr = util . getTextAtOffsetPosition ( document , selection . start , - 1 ) ; const selAfterChr = util . getTextAtOffsetPosition ( document , selection . end , 1 ) ; let addBefore = decoChar ; let addAfter = decoChar ; if ( selBeforeChr && selBeforeChr != " " ) { addBefore = ` ${ decoChar }` ; } if ( selAfterChr && selAfterChr != " " ) { addAfter = `${ decoChar } ` ; } const text = document . getText ( selection ) ; insertTextList . push ( `${ addBefore }${ text }${ addAfter }` ) ; } editor . edit ( editBuilder => { for ( let i = 0 ; i < selections . length ; i ++ ) { editBuilder . replace ( selections [ i ] , insertTextList [ i ] ) ; } } ) ; } async function _removeDecoChar ( decoType ) { function _removeDecoCharOneLine ( line , startCharacter , endCharacter ) { if ( ( startCharacter == endCharacter ) && ( endCharacter > line . length - 1 ) ) { startCharacter -- ; endCharacter -- ; } const lineSplit = line . split ( "" ) ; let regDecoChar ; if ( decoType == "bold" ) { regDecoChar = /(?<frontText>.*?)(?<frontDeco>(^|\s)\*\*)(?=\S)(?!\*{2,})(?<text>.*?)(?<=\S)(?<!\*{2,})(?<backDeco>\*\*(\s|$))(?<backText>.*)/ ; } else if ( decoType == "italic" ) { regDecoChar = /(?<frontText>.*?)(?<frontDeco>(^|\s)\*)(?=\S)(?!\*{1,})(?<text>.*?)(?<=\S)(?<!\*{1,})(?<backDeco>\*(\s|$))(?<backText>.*)/ ; } else if ( decoType == "inlineRaw" ) { regDecoChar = /(?<frontText>.*?)(?<frontDeco>(^|\s)``)(?=\S)(?!`{2,})(?<text>.*?)(?<=\S)(?<!`{2,})(?<backDeco>``(\s|$))(?<backText>.*)/ ; } else { regDecoChar = new RegExp ( "" ) ; } interface RemovedDecoCharInfo { removedChar : string ; position : ( "front" | "back" ) ; } let removedDecoCharsInfo = [ ] ; do { var match = regDecoChar . exec ( line ) ; if ( ! match ) { removedDecoCharsInfo . push ( line ) ; break ; } const matchGrp = match . groups ; if ( ! matchGrp ) { break ; } removedDecoCharsInfo . push ( matchGrp [ "frontText" ] ) ; const frontDecoText = matchGrp [ "frontDeco" ] ; for ( let i = 0 ; i < frontDecoText . length ; i ++ ) { removedDecoCharsInfo . push ( { removedChar : frontDecoText [ i ] , position : "front" } ) ; } removedDecoCharsInfo . push ( matchGrp [ "text" ] ) ; const backDecoText = matchGrp [ "backDeco" ] ; for ( let i = 0 ; i < backDecoText . length ; i ++ ) { removedDecoCharsInfo . push ( { removedChar : backDecoText [ i ] , position : "back" } ) ; } line = matchGrp [ "backText" ] ; } while ( line ) ; let removedDecoCharSplit = [ ] ; for ( const removedDecoChar of removedDecoCharsInfo ) { if ( typeof removedDecoChar == "string" ) { removedDecoCharSplit = removedDecoCharSplit . concat ( removedDecoChar . split ( "" ) ) ; } else { removedDecoCharSplit . push ( removedDecoChar ) ; } } interface AnalysisResults { analyzedForward : string [ ] ; analyzedSelRange : string [ ] ; analyzedBackward : string [ ] ; } let analysisResults = { analyzedForward : [ ] , analyzedSelRange : [ ] , analyzedBackward : [ ] } ; let needForwardContinuance = false ; let needBackwardContinuance = false ; let elementsOut ; elementsOut = removedDecoCharSplit . slice ( startCharacter , endCharacter + 1 ) ; for ( let i = 0 ; i < elementsOut . length ; i ++ ) { const element = elementsOut [ i ] ; if ( typeof element == "string" ) { analysisResults . analyzedSelRange . push ( element ) ; } else { if ( i == 0 ) { needForwardContinuance = element . position ; } if ( i == elementsOut . length - 1 ) { needBackwardContinuance = element . position ; } } } let hasRemovedDecoChar = false ; let frontExclusionStatus = null ; elementsOut = removedDecoCharSplit . slice ( 0 , startCharacter ) ; elementsOut = elementsOut . reverse ( ) ; if ( needForwardContinuance == "front" ) { frontExclusionStatus = "inExclusion" ; } for ( const element of elementsOut ) { if ( typeof element == "string" ) { analysisResults . analyzedForward . unshift ( element ) ; if ( frontExclusionStatus == "inExclusion" ) { frontExclusionStatus = "finished" ; } } else { if ( needForwardContinuance == "back" ) { if ( element . position == "front" && ! frontExclusionStatus ) { frontExclusionStatus = "inExclusion" ; } if ( frontExclusionStatus == "finished" ) { analysisResults . analyzedForward . unshift ( element . removedChar ) ; } } else { if ( hasRemovedDecoChar ) { analysisResults . analyzedForward . unshift ( element . removedChar ) ; } else if ( element . position == "back" ) { analysisResults . analyzedForward . unshift ( element . removedChar ) ; hasRemovedDecoChar = true ; } } } } hasRemovedDecoChar = false ; let backExclusionStatus = null ; elementsOut = removedDecoCharSplit . slice ( endCharacter + 1 , lineSplit . length ) ; if ( needBackwardContinuance == "back" ) { backExclusionStatus = "inExclusion" ; } for ( const element of elementsOut ) { if ( typeof element == "string" ) { analysisResults . analyzedBackward . push ( element ) ; if ( backExclusionStatus == "inExclusion" ) { backExclusionStatus = "finished" ; } } else { if ( needBackwardContinuance == "front" ) { if ( element . position == "back" && ! backExclusionStatus ) { backExclusionStatus = "inExclusion" ; } if ( backExclusionStatus == "finished" ) { analysisResults . analyzedBackward . push ( element . removedChar ) ; } } else { if ( hasRemovedDecoChar ) { analysisResults . analyzedBackward . push ( element . removedChar ) ; } else if ( element . position == "front" ) { analysisResults . analyzedBackward . push ( element . removedChar ) ; hasRemovedDecoChar = true ; } } } } let finalResult = [ ] ; finalResult = finalResult . concat ( analysisResults . analyzedForward ) ; finalResult = finalResult . concat ( analysisResults . analyzedSelRange ) ; finalResult = finalResult . concat ( analysisResults . analyzedBackward ) ; return finalResult . join ( "" ) ; } let _removed = false ; const editor = vscode . window . activeTextEditor ; if ( ! editor ) { return _removed ; } const curSelections = [ ] ; const document = editor . document ; const selections = editor . selections ; for ( const selection of selections ) { const startLine = selection . start . line ; const startChar = selection . start . character ; const startLineEndChar = document . lineAt ( startLine ) . range . end . character ; const endLine = selection . end . line ; const endChar = selection . end . character ; const endLineEndChar = document . lineAt ( endLine ) . range . end . character ; curSelections . push ( new vscode . Selection ( startLine , startChar , endLine , endChar ) ) ; const fullRange = new vscode . Range ( startLine , 0 , endLine , endLineEndChar ) ; const fullText = document . getText ( fullRange ) ; const fullLines = fullText . split ( /\r\n|\r|\n/ ) ; let newLines = [ ] ; for ( let i = 0 ; i < fullLines . length ; i ++ ) { const line = fullLines [ i ] ; if ( i == 0 ) { if ( selection . isSingleLine ) { const newLine = _removeDecoCharOneLine ( line , startChar , endChar ) ; newLines . push ( newLine ) ; if ( line != newLine ) { _removed = true ; } } else { const newLine = _removeDecoCharOneLine ( line , startChar , startLineEndChar ) ; newLines . push ( newLine ) ; if ( line != newLine ) { _removed = true ; } continue ; } } else if ( i == fullLines . length - 1 ) { const newLine = _removeDecoCharOneLine ( line , 0 , endChar ) ; newLines . push ( newLine ) ; if ( line != newLine ) { _removed = true ; } continue ; } else { const newLine = _removeDecoCharOneLine ( line , 0 , line . length ) ; newLines . push ( newLine ) ; if ( line != newLine ) { _removed = true ; } continue ; } } await editor . edit ( editBuilder => { const newText = newLines . join ( "\n" ) ; editBuilder . replace ( fullRange , newText ) ; } ) ; } editor . selections = curSelections ; return _removed ; } export function list ( ) { vscode . commands . executeCommand ( 'editor.action.triggerSuggest' ) ; } export function heading ( ) { vscode . commands . executeCommand ( 'editor.action.triggerSuggest' ) ; } export async function addHeading ( _triggerChar ) { const editor = vscode . window . activeTextEditor ; if ( ! editor ) { return ; } const document = editor . document ; const selections = editor . selections ; for ( let i = 0 ; i < selections . length ; i ++ ) { const selection = selections [ i ] ; const curSelectionLine = selection . start . line ; let insertText = `${ _triggerChar . repeat ( 5 ) }\n\n` ; if ( curSelectionLine > 0 ) { const oneLineRangeAbove = editor . document . lineAt ( curSelectionLine - 1 ) . range ; const oneLineRangeAboveEndCharNumber = oneLineRangeAbove . end . character ; if ( oneLineRangeAboveEndCharNumber > 0 ) { const lineText = document . getText ( oneLineRangeAbove ) ; const strCount = util . countTextWidth ( lineText ) ; if ( strCount > 5 ) { insertText = `${ _triggerChar . repeat ( strCount ) }\n` ; } } } const range = editor . document . lineAt ( curSelectionLine ) . range ; await editor . edit ( editBuilder => { editBuilder . replace ( range , insertText ) ; } ) ; } const selection = editor . selection ; const newSelection = new vscode . Selection ( selection . end , selection . end ) ; editor . selection = newSelection ; } export async function key_enter ( ) { const editor = vscode . window . activeTextEditor ; if ( ! editor ) { return ; } if ( util . tableIsSelected ( editor ) ) { const prevTwoLetters = util . getTextAtOffsetPosition ( editor . document , editor . selection . end , - 2 ) ; const postLetter = util . getTextAtOffsetPosition ( editor . document , editor . selection . end , 1 ) ; const prevLetter = util . getTextAtOffsetPosition ( editor . document , editor . selection . end , - 1 ) ; const table = new tableEditor . TableEditor ( editor ) ; if ( table . isSelectedFirstGrid ( ) ) { const curLine = editor . selection . start . line ; const curLineFirstChar = editor . document . lineAt ( curLine ) . range . start . character ; const newPos = new vscode . Position ( curLine , curLineFirstChar ) ; editor . selection = new vscode . Selection ( newPos , newPos ) ; _addNewLine ( ) ; return ; } else if ( table . isSelectedLastGrid ( ) ) { const curLine = editor . selection . end . line ; const curLineLastChar = editor . document . lineAt ( curLine ) . range . end . character ; const newPos = new vscode . Position ( curLine , curLineLastChar ) ; editor . selection = new vscode . Selection ( newPos , newPos ) ; _addNewLine ( ) ; return ; } if ( util . isSelectingLastChara ( ) && prevLetter == "|" ) { table . addRow ( ) ; } else if ( prevTwoLetters == "|+" || ( prevLetter == "+" && postLetter == "|" ) ) { table . addColumn ( ) ; } else if ( prevTwoLetters == "|-" || ( prevLetter == "-" && postLetter == "|" ) ) { if ( util . isSelectingLastChara ( ) ) { table . removeRow ( ) ; } else { const regStartWithMinusSymbol = /^(-\|)/ ; const curLine = editor . selection . end . line ; const curLineText = editor . document . lineAt ( curLine ) . text ; const startWithMatch = regStartWithMinusSymbol . exec ( curLineText ) ; if ( startWithMatch ) { table . removeRow ( ) ; } else { table . removeColumn ( ) ; } } } else if ( prevTwoLetters == "|>" || prevTwoLetters == ">>" || ( prevLetter == ">" && postLetter == "|" ) ) { table . moveColumn ( "right" ) ; } else if ( prevTwoLetters == "|<" || prevTwoLetters == "<<" || ( prevLetter == "<" && postLetter == "|" ) ) { table . moveColumn ( "left" ) ; } else if ( prevTwoLetters == "|^" || prevTwoLetters == "^^" || ( prevLetter == "^" && postLetter == "|" ) ) { table . moveRow ( "top" ) ; } else if ( prevTwoLetters == "|v" || prevTwoLetters == "vv" || ( prevLetter == "v" && postLetter == "|" ) ) { table . moveRow ( "bottom" ) ; } else { table . selectionChange ( "buttom" ) ; } } else if ( util . listIsSelected ( editor ) ) { } } export async function key_shift_enter ( ) { const editor = vscode . window . activeTextEditor ; if ( ! editor ) { return ; } if ( util . tableIsSelected ( editor ) ) { const table = new tableEditor . TableEditor ( editor ) ; table . selectionChange ( "top" ) ; } else if ( util . listIsSelected ( editor ) ) { } } export async function key_alt_enter ( ) { const editor = vscode . window . activeTextEditor ; if ( ! editor ) { return ; } if ( util . tableIsSelected ( editor ) && ! util . isSelectingLastChara ( ) ) { const table = new tableEditor . TableEditor ( editor ) ; table . addNewLine ( ) ; } } export async function key_tab ( ) { const editor = vscode . window . activeTextEditor ; if ( ! editor ) { return ; } if ( util . tableIsSelected ( editor ) ) { const table = new tableEditor . TableEditor ( editor ) ; table . selectionChange ( "right" ) ; } else if ( util . listIsSelected ( editor ) ) { vscode . commands . executeCommand ( 'editor.action.triggerSuggest' ) ; } else if ( util . listExistsOneLineAbove ( editor ) ) { vscode . commands . executeCommand ( 'editor.action.triggerSuggest' ) ; } else { vscode . commands . executeCommand ( "tab" ) ; } } export async function key_shift_tab ( ) { const editor = vscode . window . activeTextEditor ; if ( ! editor ) { return ; } if ( util . tableIsSelected ( editor ) ) { const table = new tableEditor . TableEditor ( editor ) ; table . selectionChange ( "left" ) ; } else if ( util . listIsSelected ( editor ) ) { } else { vscode . commands . executeCommand ( "outdent" ) ; } } async function _addNewLine ( ) { const editor = vscode . window . activeTextEditor ; if ( ! editor ) { return ; } const editOptions = { undoStopBefore : false , undoStopAfter : false } ; await editor . edit ( ( editBuilder ) => { const curPosition = editor . selection . end ; editBuilder . insert ( curPosition , "\n" ) ; } , editOptions ) ; }