mirror of
https://github.com/marktext/marktext.git
synced 2025-05-03 20:32:28 +08:00
fix: sidebar tree view (#764)
This commit is contained in:
parent
2007fc59e9
commit
eff54dcaa1
@ -190,10 +190,7 @@ class AppWindow {
|
||||
const unwatcher = this.watcher.watch(win, pathname)
|
||||
this.windows.get(win.id).watchers.push(unwatcher)
|
||||
try {
|
||||
win.webContents.send('AGANI::open-project', {
|
||||
name: path.basename(pathname),
|
||||
pathname
|
||||
})
|
||||
win.webContents.send('AGANI::open-project', pathname)
|
||||
} catch (err) {
|
||||
log(err)
|
||||
}
|
||||
|
@ -19,8 +19,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import path from 'path'
|
||||
import { mapState } from 'vuex'
|
||||
import { fileMixins } from '../../mixins'
|
||||
import { PATH_SEPARATOR } from '../../config'
|
||||
|
||||
export default {
|
||||
mixins: [fileMixins],
|
||||
@ -36,14 +38,21 @@
|
||||
tabs: state => state.editor.tabs,
|
||||
currentFile: state => state.editor.currentFile
|
||||
}),
|
||||
|
||||
// Return filename without extension.
|
||||
filename () {
|
||||
return this.file.name.split('.')[0]
|
||||
return path.basename(this.file.name, path.extname(this.file.name))
|
||||
},
|
||||
|
||||
// Return the filename extension or null.
|
||||
extension () {
|
||||
return `.${this.file.name.split('.')[1]}`
|
||||
return path.extname(this.file.name)
|
||||
},
|
||||
|
||||
// Return the parent directory with trailing path separator.
|
||||
// NOTE: Parent from "Z:\" is "Z:\" on Windows!
|
||||
parent () {
|
||||
return this.file.pathname.match(/(?:^|\/)([^/]+)\/[^/]+\.[^/]+$/)[1] + '/'
|
||||
return path.join(path.dirname(this.file.pathname), PATH_SEPARATOR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,7 @@
|
||||
import { remote } from 'electron'
|
||||
import { mapState } from 'vuex'
|
||||
import { minimizePath, restorePath, maximizePath, closePath } from '../assets/window-controls.js'
|
||||
import { PATH_SEPARATOR } from '../config'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
@ -134,7 +135,7 @@
|
||||
}),
|
||||
paths () {
|
||||
if (!this.pathname) return []
|
||||
const pathnameToken = this.pathname.split('/').filter(i => i)
|
||||
const pathnameToken = this.pathname.split(PATH_SEPARATOR).filter(i => i)
|
||||
return pathnameToken.slice(0, pathnameToken.length - 1).slice(-3)
|
||||
}
|
||||
},
|
||||
|
@ -1,3 +1,7 @@
|
||||
import path from 'path'
|
||||
|
||||
export const PATH_SEPARATOR = path.sep
|
||||
|
||||
export const THEME_LINK_ID = 'ag-theme'
|
||||
export const COMMON_STYLE_ID = 'ag-common-style'
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { ipcRenderer, shell } from 'electron'
|
||||
import { addFile, unlinkFile, changeFile, addDirectory, unlinkDirectory } from './treeCtrl'
|
||||
import bus from '../bus'
|
||||
import { create, paste, rename } from '../util/fileSystem'
|
||||
import { PATH_SEPARATOR } from '../config'
|
||||
import notice from '../services/notification'
|
||||
import { getFileStateFromData } from './help'
|
||||
|
||||
@ -36,9 +37,17 @@ const getters = {
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_PROJECT_TREE (state, { pathname, name }) {
|
||||
SET_PROJECT_TREE (state, pathname) {
|
||||
let name = path.basename(pathname)
|
||||
if (!name) {
|
||||
// Root directory such "/" or "C:\"
|
||||
name = pathname
|
||||
}
|
||||
|
||||
state.projectTree = {
|
||||
pathname,
|
||||
// Root full path
|
||||
pathname: path.normalize(pathname),
|
||||
// Root directory name
|
||||
name,
|
||||
isDirectory: true,
|
||||
isFile: false,
|
||||
@ -105,12 +114,12 @@ const mutations = {
|
||||
|
||||
const actions = {
|
||||
LISTEN_FOR_LOAD_PROJECT ({ commit, dispatch }) {
|
||||
ipcRenderer.on('AGANI::open-project', (e, { pathname, name }) => {
|
||||
ipcRenderer.on('AGANI::open-project', (e, pathname) => {
|
||||
// Initialize editor and show empty/new tab
|
||||
dispatch('NEW_BLANK_FILE')
|
||||
|
||||
dispatch('INIT_STATUS', true)
|
||||
commit('SET_PROJECT_TREE', { pathname, name })
|
||||
commit('SET_PROJECT_TREE', pathname)
|
||||
commit('SET_LAYOUT', {
|
||||
rightColumn: 'files',
|
||||
showSideBar: true,
|
||||
@ -189,7 +198,7 @@ const actions = {
|
||||
const { pathname, isDirectory } = state.activeItem
|
||||
const dirname = isDirectory ? pathname : path.dirname(pathname)
|
||||
if (clipboard) {
|
||||
clipboard.dest = dirname + '/' + path.basename(clipboard.src)
|
||||
clipboard.dest = dirname + PATH_SEPARATOR + path.basename(clipboard.src)
|
||||
paste(clipboard)
|
||||
.then(() => {
|
||||
commit('SET_CLIPBOARD', null)
|
||||
@ -232,7 +241,7 @@ const actions = {
|
||||
RENAME_IN_SIDEBAR ({ commit, state }, name) {
|
||||
const src = state.renameCache
|
||||
const dirname = path.dirname(src)
|
||||
const dest = dirname + '/' + name
|
||||
const dest = dirname + PATH_SEPARATOR + name
|
||||
rename(src, dest)
|
||||
.then(() => {
|
||||
commit('RENAME_IF_NEEDED', { src, dest })
|
||||
|
@ -1,34 +1,43 @@
|
||||
import path from 'path'
|
||||
import { getUniqueId } from '../util'
|
||||
import { PATH_SEPARATOR } from '../config'
|
||||
|
||||
const pathSeparator = path.sep
|
||||
const isWindows = process.platform === 'win32'
|
||||
|
||||
const getPathArr = (projectName, pathname) => {
|
||||
const reUnix = /^\/+(.*)/
|
||||
const reWindows = /^\\+(.*)/
|
||||
let [prePath, partPath] = pathname.split(projectName)
|
||||
partPath = partPath.replace(isWindows ? reWindows : reUnix, (_, p1) => p1)
|
||||
prePath += projectName
|
||||
const tokens = partPath ? partPath.split(pathSeparator) : []
|
||||
|
||||
return { prePath, tokens }
|
||||
/**
|
||||
* Return all sub-directories relative to the root directory.
|
||||
*
|
||||
* @param {string} rootPath Root directory path
|
||||
* @param {string} pathname Full directory path
|
||||
* @returns {Array<string>} Sub-directories relative to root.
|
||||
*/
|
||||
const getSubdirectoriesFromRoot = (rootPath, pathname) => {
|
||||
if (!path.isAbsolute(pathname)) {
|
||||
throw new Error('Invalid path!')
|
||||
}
|
||||
const relativePath = path.relative(rootPath, pathname)
|
||||
return relativePath ? relativePath.split(PATH_SEPARATOR) : []
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new file to the tree list.
|
||||
*
|
||||
* @param {*} tree Root file tree
|
||||
* @param {*} file The file that should be added
|
||||
*/
|
||||
export const addFile = (tree, file) => {
|
||||
const { pathname, name } = file
|
||||
const dirname = path.dirname(pathname)
|
||||
let { prePath, tokens } = getPathArr(tree.name, dirname)
|
||||
let folder = tree
|
||||
let folders = tree.folders
|
||||
const subDirectories = getSubdirectoriesFromRoot(tree.pathname, dirname)
|
||||
|
||||
for (const token of tokens) {
|
||||
let childFolder = folders.find(f => f.name === token)
|
||||
let currentPath = tree.pathname
|
||||
let currentFolder = tree
|
||||
let currentSubFolders = tree.folders
|
||||
for (const directoryName of subDirectories) {
|
||||
let childFolder = currentSubFolders.find(f => f.name === directoryName)
|
||||
if (!childFolder) {
|
||||
childFolder = {
|
||||
id: getUniqueId(),
|
||||
pathname: `${prePath}${pathSeparator}${token}`,
|
||||
name: token,
|
||||
pathname: `${currentPath}${PATH_SEPARATOR}${directoryName}`,
|
||||
name: directoryName,
|
||||
isCollapsed: true,
|
||||
isDirectory: true,
|
||||
isFile: false,
|
||||
@ -36,30 +45,39 @@ export const addFile = (tree, file) => {
|
||||
folders: [],
|
||||
files: []
|
||||
}
|
||||
folders.push(childFolder)
|
||||
currentSubFolders.push(childFolder)
|
||||
}
|
||||
prePath = `${prePath}${pathSeparator}${token}`
|
||||
folder = childFolder
|
||||
folders = childFolder.folders
|
||||
|
||||
currentPath = `${currentPath}${PATH_SEPARATOR}${directoryName}`
|
||||
currentFolder = childFolder
|
||||
currentSubFolders = childFolder.folders
|
||||
}
|
||||
|
||||
if (!folder.files.find(f => f.name === name)) {
|
||||
// Add file to related directory
|
||||
if (!currentFolder.files.find(f => f.name === name)) {
|
||||
file.id = getUniqueId()
|
||||
folder.files.push(file)
|
||||
currentFolder.files.push(file)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new directory to the tree list.
|
||||
*
|
||||
* @param {*} tree Root file tree
|
||||
* @param {*} dir The directory that should be added
|
||||
*/
|
||||
export const addDirectory = (tree, dir) => {
|
||||
let { prePath, tokens } = getPathArr(tree.name, dir.pathname)
|
||||
let folders = tree.folders
|
||||
const subDirectories = getSubdirectoriesFromRoot(tree.pathname, dir.pathname)
|
||||
|
||||
for (const token of tokens) {
|
||||
let childFolder = folders.find(f => f.name === token)
|
||||
let currentPath = tree.pathname
|
||||
let currentSubFolders = tree.folders
|
||||
for (const directoryName of subDirectories) {
|
||||
let childFolder = currentSubFolders.find(f => f.name === directoryName)
|
||||
if (!childFolder) {
|
||||
childFolder = {
|
||||
id: getUniqueId(),
|
||||
pathname: `${prePath}${pathSeparator}${token}`,
|
||||
name: token,
|
||||
pathname: `${currentPath}${PATH_SEPARATOR}${directoryName}`,
|
||||
name: directoryName,
|
||||
isCollapsed: true,
|
||||
isDirectory: true,
|
||||
isFile: false,
|
||||
@ -67,70 +85,86 @@ export const addDirectory = (tree, dir) => {
|
||||
folders: [],
|
||||
files: []
|
||||
}
|
||||
folders.push(childFolder)
|
||||
currentSubFolders.push(childFolder)
|
||||
}
|
||||
prePath = `${prePath}${pathSeparator}${token}`
|
||||
folders = childFolder.folders
|
||||
|
||||
currentPath = `${currentPath}${PATH_SEPARATOR}${directoryName}`
|
||||
currentSubFolders = childFolder.folders
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given file from the tree list.
|
||||
*
|
||||
* @param {*} tree Root file tree
|
||||
* @param {*} file The file that should be deleted
|
||||
*/
|
||||
export const unlinkFile = (tree, file) => {
|
||||
const { pathname } = file
|
||||
const dirname = path.dirname(pathname)
|
||||
let { tokens } = getPathArr(tree.name, dirname)
|
||||
const subDirectories = getSubdirectoriesFromRoot(tree.pathname, dirname)
|
||||
|
||||
let folder = tree
|
||||
let folders = tree.folders
|
||||
|
||||
for (const token of tokens) {
|
||||
const childFolder = folders.find(f => f.name === token)
|
||||
let currentFolder = tree
|
||||
let currentSubFolders = tree.folders
|
||||
for (const directoryName of subDirectories) {
|
||||
const childFolder = currentSubFolders.find(f => f.name === directoryName)
|
||||
if (!childFolder) return
|
||||
folder = childFolder
|
||||
folders = childFolder.folders
|
||||
currentFolder = childFolder
|
||||
currentSubFolders = childFolder.folders
|
||||
}
|
||||
|
||||
const index = folder.files.findIndex(f => f.pathname === pathname)
|
||||
|
||||
if (index > -1) {
|
||||
folder.files.splice(index, 1)
|
||||
const index = currentFolder.files.findIndex(f => f.pathname === pathname)
|
||||
if (index !== -1) {
|
||||
currentFolder.files.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a given file in the tree list.
|
||||
*
|
||||
* @param {*} tree Root file tree
|
||||
* @param {*} file The file that was changed
|
||||
*/
|
||||
export const changeFile = (tree, file) => {
|
||||
const { pathname, data } = file
|
||||
const dirname = path.dirname(pathname)
|
||||
let { tokens } = getPathArr(tree.name, dirname)
|
||||
const subDirectories = getSubdirectoriesFromRoot(tree.pathname, dirname)
|
||||
|
||||
let folder = tree
|
||||
let folders = tree.folders
|
||||
|
||||
for (const token of tokens) {
|
||||
const childFolder = folders.find(f => f.name === token)
|
||||
let currentFolder = tree
|
||||
let currentSubFolders = tree.folders
|
||||
for (const directoryName of subDirectories) {
|
||||
const childFolder = currentSubFolders.find(f => f.name === directoryName)
|
||||
if (!childFolder) return
|
||||
folder = childFolder
|
||||
folders = childFolder.folders
|
||||
currentFolder = childFolder
|
||||
currentSubFolders = childFolder.folders
|
||||
}
|
||||
|
||||
const index = folder.files.findIndex(f => f.pathname === pathname)
|
||||
folder.files[index].data = data
|
||||
const index = currentFolder.files.findIndex(f => f.pathname === pathname)
|
||||
if (index !== -1) {
|
||||
currentFolder.files[index].data = data
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given directory from the tree list.
|
||||
*
|
||||
* @param {*} tree Root file tree
|
||||
* @param {*} dir The directory that should be deleted
|
||||
*/
|
||||
export const unlinkDirectory = (tree, dir) => {
|
||||
const { pathname } = dir
|
||||
const { tokens } = getPathArr(tree.name, pathname)
|
||||
const subDirectories = getSubdirectoriesFromRoot(tree.pathname, pathname)
|
||||
|
||||
tokens.pop()
|
||||
let folders = tree.folders
|
||||
|
||||
for (const token of tokens) {
|
||||
const childFolder = folders.find(f => f.name === token)
|
||||
subDirectories.pop()
|
||||
let currentFolder = tree.folders
|
||||
for (const directoryName of subDirectories) {
|
||||
const childFolder = currentFolder.find(f => f.name === directoryName)
|
||||
if (!childFolder) return
|
||||
folders = childFolder.folders
|
||||
currentFolder = childFolder.folders
|
||||
}
|
||||
|
||||
const index = folders.findIndex(f => f.pathname === pathname)
|
||||
|
||||
if (index > -1) {
|
||||
folders.splice(index, 1)
|
||||
const index = currentFolder.findIndex(f => f.pathname === pathname)
|
||||
if (index !== -1) {
|
||||
currentFolder.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user